问题
I am working with apache camel and would like to add certain keys to my logs using MDC. I went through the official Camel MDC Logging documentation which is pretty great. I am able to log my routeId's without much effort. I also need to add a field from Camel's Body.
Worst case scenario I can add this manually in all routes, but I was wondering if its possible to add fields from body to MDC in a easier fashion?
Any ideas are appreciated. I would really like to be able to do this without having to go into every route and adding a one liner.
Update:
Implemented a custom MDCUnitOfWork and Factory in my project. I am able to see the CustomUnitOfWorkFactory creating my CustomUnitOfWork which is then setting the MDC values.
However I noticed this only happens in the beginning of the route.
In my use case, I am Polling an Amazon SQS as my first route. I do not have the required information here. In the first route I build my Context and set that to Camel body which is where my information that I need to set in MDC resides.
Is it possible to create UnitOfWork before second route as well?
回答1:
You can configure a custom UnitOfWorkFactory
to create a custom UnitOfWork
that extends the MDCUnitOfWork
, where you can add custom information to MDC.
- http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/impl/MDCUnitOfWork.html
- http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/spi/UnitOfWorkFactory.html
You can configure the UnitOfWorkFactory on CamelContext from Java or in XML just add a <bean>
and Camel detects and uses it
- http://camel.apache.org/advanced-configuration-of-camelcontext-using-spring.html
回答2:
Here is a full implementation with code based on Claus's recommendation. We are using spring boot, but adjust according to your needs
Auto register a simple bean
@Bean
public CamelContextConfiguration contextConfiguration() {
return new CamelContextConfiguration() {
@Override
public void beforeApplicationStart(CamelContext context) {
context.setUseMDCLogging(true);
context.setUnitOfWorkFactory(MyUnitOfWork::new);
}
@Override
public void afterApplicationStart(CamelContext camelContext) {
}
};
}
Then, create your custom unit of work class
public class MyUnitOfWork extends MDCUnitOfWork {
public MyUnitOfWork(Exchange exchange) {
super(exchange);
if( exchange.getProperty("myProp") != null){
MDC.put("myProp", (String) exchange.getProperty("myProp"));
}
}
}
In your logback/log4j configuration use the value myProp like so:
%X{myProp}
It should start logging
回答3:
I had an issue with missing headers in MDC and the solutions provided here in earlier comments didn't work. Here's a working solution to my case: all headers except default headers are being lost between threads (MDC is thread local). Because of Camel MDCUnitOfWork default flawed implementation the only headers passed to an MDC in another thread are default Camel headers.
You have to setMDCLogging to true and implement your own UnitOfWork like it was shown in previous comments:
camelContext.setUseMDCLogging(true);
camelContext.setUnitOfWorkFactory(CustomUnitOfWork::new);
The implementation of the CustomUnitOfWork is a lot more complicated, because we need to override a lot of deafult MDCUnitOfWork behavior (make sure to import org.slf4j.MDC):
class CustomUnitOfWork extends MDCUnitOfWork {
static final String CUSTOM_FIELD_NAME = "customField";
private String customField;
CustomUnitOfWork(Exchange exchange) {
super(exchange);
customField = (String) exchange.getIn().getHeader("customFieldPath123");
MDC.put(CUSTOM_FIELD_NAME, customField);
}
@Override
public UnitOfWork newInstance(Exchange exchange) {
return new CustomUnitOfWork(exchange);
}
@Override
public void clear() {
super.clear();
if (customField != null) {
MDC.put(CUSTOM_FIELD_NAME, customField);
} else {
MDC.remove(CUSTOM_FIELD_NAME);
}
}
@Override
public void stop() throws Exception {
super.stop();
clear();
}
@Override
public AsyncCallback beforeProcess(Processor processor, Exchange exchange, AsyncCallback callback) {
return new CustomMDCCallback(callback);
}
private class CustomMDCCallback implements AsyncCallback {
private final AsyncCallback delegate;
private final String breadcrumbId;
private final String exchangeId;
private final String messageId;
private final String correlationId;
private final String routeId;
private final String camelContextId;
private final String customField;
private CustomMDCCallback(AsyncCallback delegate) {
this.delegate = delegate;
this.exchangeId = MDC.get(MDC_EXCHANGE_ID);
this.messageId = MDC.get(MDC_MESSAGE_ID);
this.breadcrumbId = MDC.get(MDC_BREADCRUMB_ID);
this.correlationId = MDC.get(MDC_CORRELATION_ID);
this.camelContextId = MDC.get(MDC_CAMEL_CONTEXT_ID);
this.routeId = MDC.get(MDC_ROUTE_ID);
this.customField = MDC.get(CUSTOM_FIELD_NAME);
}
@Override
public void done(boolean doneSync) {
try {
if (!doneSync) {
checkAndPut(breadcrumbId, MDC_BREADCRUMB_ID);
checkAndPut(exchangeId, MDC_EXCHANGE_ID);
checkAndPut(messageId, MDC_MESSAGE_ID);
checkAndPut(correlationId, MDC_CORRELATION_ID);
checkAndPut(camelContextId, MDC_CAMEL_CONTEXT_ID);
checkAndPut(customField, CUSTOM_FIELD_NAME);
}
checkAndPut(routeId, MDC_ROUTE_ID);
} finally {
delegate.done(doneSync);
}
}
private void checkAndPut(String value, String fieldName) {
if (value != null) {
MDC.put(fieldName, value);
}
}
}
}
If you take a look at Camel MDCUnitOfWork class you will see that the code is very similar.
回答4:
we wanted a similar thing from our Camel routes - to log particular properties and headers using MDC. Unfortunately our routes were transacted and the CustomMDCUnitOfWork was not kicking in. We ended up implementing an org.apache.camel.spi.InterceptStrategy in order to add the MDC values. If there is a better way of doing this with transacted routes I would be happy to know..!
来源:https://stackoverflow.com/questions/29045419/apache-camel-mdc-add-field-from-body