Tracer InterceptorCamel supports a tracer interceptor that is used for logging the route executions at INFO level. The Tracer is an InterceptStrategy which can be applied to a DefaultCamelContext or SpringCamelContext to ensure that there is a TracerInterceptor created for every node in the DSL. You can enable or disable the Tracer's logging dynamically, by calling the tracer's setEnabled method.
From Camel 2.12 onwards you must explicit enable tracing on CamelContext to use it. In XML DSL you do this by setting <camelContext trace="true" ...> and in Java you do camelContext.setTracing(true). Options
FormattingThe tracer formats the execution of exchanges to log lines. They are logged at INFO level in the log category: org.apache.camel.processor.interceptor.TraceInterceptor. DefaultTraceFormatter has the following options:
Example:
ID-claus-acer/4412-1222625653890/2-0 -> to(mock:a) , Pattern:InOnly , Headers:{to=James} , BodyType:String , Body:Hello London
ID-claus-acer/3690-1214458315718/2-0 is the breadcrumb with the unique correlation id. Showing from and toThe trace log will output both the from and to so you can see where the Exchange came from, such as: >>> direct:start --> process(MyProcessor) >>> process(MyProcessor) --> to(mock:a) >>> to(mock:a) --> to(mock:b) EnablingTo enable tracer from the main run java org.apache.camel.spring.Main -t or java org.apache.camel.spring.Main -trace and the tracer will be active. Enabling from Java DSL
context.setTracing(true);
You can configure tracing at a higher granularity as you can configure it on camel context and then override and set it per route as well. For instance you could just enable tracer for one particular route.
INFO TraceInterceptor - ID-davsclaus-local-54403-1246038742624-0-0 >>> from(direct:start) --> MyProcessor , Pattern:InOnly, Headers:{to=James}, BodyType:String, Body:Hello London
INFO TraceInterceptor - ID-davsclaus-local-54403-1246038742624-0-0 >>> MyProcessor --> mock:a , Pattern:InOnly, Headers:{to=James}, BodyType:String, Body:Hello London
INFO TraceInterceptor - ID-davsclaus-local-54403-1246038742624-0-0 >>> mock:a --> mock:b , Pattern:InOnly, Headers:{to=James}, BodyType:String, Body:Hello London
...
INFO TraceInterceptor - ID-davsclaus-local-54403-1246038742624-0-1 >>> from(direct:start) --> MyProcessor , Pattern:InOnly, Headers:{from=Claus}, BodyType:String, Body:This is Copenhagen calling
INFO TraceInterceptor - ID-davsclaus-local-54403-1246038742624-0-1 >>> MyProcessor --> mock:a , Pattern:InOnly, Headers:{from=Claus}, BodyType:String, Body:This is Copenhagen calling
INFO TraceInterceptor - ID-davsclaus-local-54403-1246038742624-0-1 >>> mock:a --> mock:b , Pattern:InOnly, Headers:{from=Claus}, BodyType:String, Body:This is Copenhagen calling
Configuring from Java DSLThe tracer options can be configured from the Java DSL like this:
public void configure() throws Exception {
// add tracer as an interceptor so it will log the exchange executions at runtime
// this can aid us to understand/see how the exchanges is routed etc.
Tracer tracer = new Tracer();
formatter.getDefaultTraceFormatter().setShowBreadCrumb(false);
formatter.getDefaultTraceFormatter().setShowNode(false);
...
getContext().addInterceptStrategy(tracer);
Using predicates to filter exchangesIn the code below we want the tracer only to trace if the body contains the text London. As this is just an example can of course set any Predicate that matches your criteria:
Tracer tracer = new Tracer();
// set the level to FATAL so we can easily spot it
tracer.setLogLevel(LoggingLevel.FATAL);
// and only trace if the body contains London as text
tracer.setTraceFilter(body().contains(constant("London")));
Enabling from Spring XMLThere is now a trace attribute you can specify on the *<camelContext/> for example <camelContext trace="true" xmlns="http://activemq.apache.org/camel/schema/spring"> ... </camelContext> You can see this in action with the SpringTraceTest and its spring.xml file Another option is to just include a spring XML which defines the Tracer bean such as the one that is automatically included if you run the Main with -t above. Configuration from SpringYou can configure the tracer as a Spring bean. Just add a bean with the bean class org.apache.camel.processor.interceptor.Tracer and Camel will use it as the Tracer. <!-- we can configure the tracer by defining the tracer bean and set the properties as we like --> <!-- the id name can be anything its the class that must be org.apache.camel.processor.interceptor.Tracer --> <bean id="camelTracer" class="org.apache.camel.processor.interceptor.Tracer"> <property name="traceExceptions" value="false"/> <property name="traceInterceptors" value="true"/> <property name="logLevel" value="ERROR"/> <property name="logName" value="com.mycompany.messages"/> </bean> <camelContext trace="true" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="direct:start"/> <to uri="mock:result"/> </route> </camelContext> You can configure the formatting of tracer as a Spring bean. Just add a bean with the id traceFormatter and Camel will lookup this id and use the formatter, as the example below illustrates: <bean id="traceFormatter" class="org.apache.camel.processor.interceptor.DefaultTraceFormatter"> <property name="showBody" value="true"/> <property name="showBodyType" value="false"/> <property name="showBreadCrumb" value="false"/> <property name="maxChars" value="100"/> </bean> <camelContext trace="true" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="direct:start"/> <to uri="mock:result"/> </route> </camelContext> Enable tracing of out messagesYou can trace messages coming out of processing steps. To enable this, configure the tracer as follows Java DSL Tracer tracer = new Tracer(); tracer.setTraceOutExchanges(true); // we configure the default trace formatter where we can // specify which fields we want in the output DefaultTraceFormatter formatter = new DefaultTraceFormatter(); formatter.setShowOutBody(true); formatter.setShowOutBodyType(true); // set to use our formatter tracer.setFormatter(formatter); getContext().addInterceptStrategy(tracer); or Spring DSL <bean id="camelTracer" class="org.apache.camel.processor.interceptor.Tracer"> <property name="traceOutExchanges" value="true" /> </bean> <bean id="traceFormatter" class="org.apache.camel.processor.interceptor.DefaultTraceFormatter"> <property name="showOutBody" value="true" /> <property name="showOutBodyType" value="true" /> </bean> Running with these options, you'll get output similar to:
INFO TraceInterceptor - ID-mojo/59899-1225474989226/2-0 -> transform(body) , Pattern:InOnly , Headers:{to=James} , BodyType:String , Body:Hello London
INFO TraceInterceptor - transform(body) -> ID-mojo/59899-1225474989226/2-0 , Pattern:InOnly , Headers:{to=James} , BodyType:String , Body:Hello London , OutBodyType:String , OutBody:Hello London
Using Custom FormatterYou can now implement your own org.apache.camel.processor.interceptor.TraceFormatter to be used for logging trace messages to the log. The sample below shows how to configure a Tracer from Java DSL using custom formatter: // we create a tracer where we want to use our own formatter instead of the default one Tracer tracer = new Tracer(); // use our own formatter instead of the default one MyTraceFormatter formatter = new MyTraceFormatter(); tracer.setFormatter(formatter); // and we must remeber to add the tracer to Camel getContext().addInterceptStrategy(tracer); And here we have our custom logger that implements the TraceFormatter interface where we can construct the log message how we like: // here we have out own formatter where we can create the output we want for trace logs // as this is a test we just create a simple string with * around the body class MyTraceFormatter implements TraceFormatter { public Object format(TraceInterceptor interceptor, ProcessorDefinition<?> node, Exchange exchange) { return "***" + exchange.getIn().getBody(String.class) + "***"; } } Using Destination for custom processing and routingTracer supports custom processing of trace events. This can be used to route a trace event to a JPA endpoint for persistence in a database. This works by Camel creates a new TraceEventMessage containing:
Camel routes the TraceEventMessage synchronously from the point of interception. When its completed Camel will continue routing the original Exchange. The sample below demonstrates this feature, where we route traced Exchanges to the direct:traced route: // we create a tracer where we want to send TraveEvents to an endpoint // "direct:traced" where we can do some custom processing such as storing // it in a file or a database Tracer tracer = new Tracer(); tracer.setDestinationUri("direct:traced"); // we disable regular trace logging in the log file. You can omit this and // have both. tracer.setLogLevel(LoggingLevel.OFF); // and we must remember to add the tracer to Camel getContext().addInterceptStrategy(tracer); Then we can configure a route for the traced messages: from("direct:traced").process(new MyTraceMessageProcessor()).to("file://myapp/logs/trace); And our processor where we can process the TraceEventMessage. Here we want to create a CSV format of the trace event to be stored as a file. We do this by constructing the CSV String and the replace the IN body with our String instead of the TraceEventMessage. class MyTraceMessageProcessor implements Processor { public void process(Exchange exchange) throws Exception { // here we can transform the message how we like want it TraceEventMessage msg = exchange.getIn().getBody(DefaultTraceEventMessage.class); // we want to store it as a CSV with from;to;exchangeId;body String s = msg.getFromEndpointUri() + ";" + msg.getToNode() + ";" + msg.getExchangeId() + ";" + msg.getBody(); // so we replace the IN body with our CSV string exchange.getIn().setBody(s); } } Using JPA as datastore for trace messagesSee Tracer Example for complete documentation and how to use this feature. Traced route path during runtimeTracer also traces the actual route path taken during runtime. Camel will store the route path taken on the UnitOfWork when Tracer is enabled. The example below demonstrates how we can use that for error handling where we can determine at which node in the route graph the error triggered. protected RouteBuilder createRouteBuilder() throws Exception { return new RouteBuilder() { public void configure() throws Exception { errorHandler(deadLetterChannel("mock:error").redeliveryDelay(0).maximumRedeliveries(3)); // must enable tracer to trace the route path taken during runtime context.setTracing(true); // let our my error processor handle all exceptions onException(Exception.class).handled(true).process(new MyErrorProcessor()); // our route where an exception can be thrown from either foo or bar bean // so we have enable tracing so we can check it at runtime to get the actual // node path taken from("direct:start").to("bean:foo").to("bean:bar"); } }; } And then our custom error processor where we can handle the exception and figure out at which node the exception occurred. private static class MyErrorProcessor implements Processor { public void process(Exchange exchange) throws Exception { TracedRouteNodes traced = exchange.getUnitOfWork().getTracedRouteNodes(); // get the list of intercepted nodes List<RouteNode> list = traced.getNodes(); // get the 3rd last as its the bean Processor last = list.get(list.size() - 3).getProcessor(); // wrapped by JMX if (last instanceof InstrumentationProcessor) { InstrumentationProcessor ip = (InstrumentationProcessor) last; last = ip.getProcessor(); } // set error message exchange.getOut().setFault(true); exchange.getOut().setBody("Failed at: " + last.toString()); } public String toString() { return "MyErrorProcessor"; } } See Also |