Chapter 12. System Management
Chapter 12. System Management Abstract The system management patterns describe how to monitor, test, and administer a messaging system. 12.1. Detour Detour The Detour from the Chapter 3, Introducing Enterprise Integration Patterns allows you to send messages through additional steps if a control condition is met. It can be useful for turning on extra validation, testing, debugging code when needed. Example In this example we essentially have a route like from("direct:start").to("mock:result") with a conditional detour to the mock:detour endpoint in the middle of the route.. Using the Spring XML Extensions whether the detour is turned on or off is decided by the ControlBean . So, when the detour is on the message is routed to mock:detour and then mock:result . When the detour is off, the message is routed to mock:result . For full details, check the example source here: camel-core/src/test/java/org/apache/camel/processor/DetourTest.java 12.2. LogEIP Overview Apache Camel provides several ways to perform logging in a route: Using the log DSL command. Using the Log component, which can log the message content. Using the Tracer, which traces message flow. Using a Processor or a Bean endpoint to perform logging in Java. Difference between the log DSL command and the log component The log DSL is much lighter and meant for logging human logs such as Starting to do ... . It can only log a message based on the Simple language. In contrast, the Log component is a fully featured logging component. The Log component is capable of logging the message itself and you have many URI options to control the logging. Java DSL example Since Apache Camel 2.2 , you can use the log DSL command to construct a log message at run time using the Simple expression language. For example, you can create a log message within a route, as follows: This route constructs a String format message at run time. The log message will by logged at INFO level, using the route ID as the log name. By default, routes are named consecutively, route-1 , route-2 and so on. But you can use the DSL command, routeId("myCoolRoute") , to specify a custom route ID. The log DSL also provides variants that enable you to set the logging level and the log name explicitly. For example, to set the logging level explicitly to LoggingLevel.DEBUG , you can invoke the log DSL as follows: The log DSL has overloaded methods to set the logging level and/or name as well. To set the log name to fileRoute , you can invoke the log DSL as follows: XML DSL example In XML DSL, the log DSL is represented by the log element and the log message is specified by setting the message attribute to a Simple expression, as follows: The log element supports the message , loggingLevel and logName attributes. For example: Global Log Name The route ID is used as the the default log name. Since Apache Camel 2.17 the log name can be changed by configuring a logname parameter. Java DSL, configure the log name based on the following example: In XML, configure the log name in the following way: If you have more than one log and you want to have the same log name on all of them, you must add the configuration to each log. 12.3. Wire Tap Wire Tap The wire tap pattern, as shown in Figure 12.1, "Wire Tap Pattern" , enables you to route a copy of the message to a separate tap location, while the original message is forwarded to the ultimate destination. Figure 12.1. Wire Tap Pattern Streams If you WireTap a stream message body, you should consider enabling Stream Caching to ensure the message body can be re-read. See more details at Stream Caching WireTap node Apache Camel 2.0 introduces the wireTap node for doing wire taps. The wireTap node copies the original exchange to a tapped exchange, whose exchange pattern is set to InOnly , because the tapped exchange should be propagated in a oneway style. The tapped exchange is processed in a separate thread, so that it can run concurrently with the main route. The wireTap supports two different approaches to tapping an exchange: Tap a copy of the original exchange. Tap a new exchange instance, enabling you to customize the tapped exchange. Note From Camel 2.16, the Wire Tap EIP emits event notifications when you send the exchange to the wire tap destination. Note As of Camel 2.20 , the Wire Tap EIP will complete any inflight wire tapped exchanges while shutting down. Tap a copy of the original exchange Using the Java DSL: Using Spring XML extensions: Tap and modify a copy of the original exchange Using the Java DSL, Apache Camel supports using either a processor or an expression to modify a copy of the original exchange. Using a processor gives you full power over how the exchange is populated, because you can set properties, headers and so on. The expression approach can only be used to modify the In message body. For example, to modify a copy of the original exchange using the processor approach: And to modify a copy of the original exchange using the expression approach: Using the Spring XML extensions, you can modify a copy of the original exchange using the processor approach, where the processorRef attribute references a spring bean with the myProcessor ID: And to modify a copy of the original exchange using the expression approach: Tap a new exchange instance You can define a wiretap with a new exchange instance by setting the copy flag to false (the default is true ). In this case, an initially empty exchange is created for the wiretap. For example, to create a new exchange instance using the processor approach: Where the second wireTap argument sets the copy flag to false , indicating that the original exchange is not copied and an empty exchange is created instead. To create a new exchange instance using the expression approach: Using the Spring XML extensions, you can indicate that a new exchange is to be created by setting the wireTap element's copy attribute to false . To create a new exchange instance using the processor approach, where the processorRef attribute references a spring bean with the myProcessor ID, as follows: And to create a new exchange instance using the expression approach: Sending a new Exchange and set headers in DSL Available as of Camel 2.8 If you send a new messages using the Section 12.3, "Wire Tap" then you could only set the message body using an Part II, "Routing Expression and Predicate Languages" from the DSL. If you also need to set new headers you would have to use a Section 1.5, "Processors" for that. So in Camel 2.8 onwards we have improved this situation so you can now set headers as well in the DSL. The following example sends a new message which has "Bye World" as message body a header with key "id" with the value 123 a header with key "date" which has current date as value Java DSL XML DSL The XML DSL is slightly different than Java DSL as how you configure the message body and headers. In XML you use <body> and <setHeader> as shown: Using URIs Wire Tap supports static and dynamic endpoint URIs. Static endpoint URIs are available as of Camel 2.20 . The following example displays how to wire tap to a JMS queue where the header ID is a part of the queue name. For more information about dynamic endpoint URIs, see the section called "Dynamic To" . Using onPrepare to execute custom logic when preparing messages Available as of Camel 2.8 For details, see Section 8.13, "Multicast" . Options The wireTap DSL command supports the following options: Name Default Value Description uri The endpoint uri where to send the wire tapped message. You should use either uri or ref . ref Refers to the endpoint where to send the wire tapped message. You should use either uri or ref . executorServiceRef Refers to a custom Section 2.8, "Threading Model" to be used when processing the wire tapped messages. If not set then Camel uses a default thread pool. processorRef Refers to a custom Section 1.5, "Processors" to be used for creating a new message (eg the send a new message mode). See below. copy true Camel 2.3 : Should a copy of the the section called "Exchanges" to used when wire tapping the message. onPrepareRef Camel 2.8: Refers to a custom Section 1.5, "Processors" to prepare the copy of the the section called "Exchanges" to be wire tapped. This allows you to do any custom logic, such as deep-cloning the message payload if that's needed etc.
from("direct:start").choice() .when().method("controlBean", "isDetour").to("mock:detour").end() .to("mock:result"); <route> <from uri="direct:start"/> <choice> <when> <method bean="controlBean" method="isDetour"/> <to uri="mock:detour"/> </when> </choice> <to uri="mock:result"/> </split> </route> from("direct:start").log("Processing USD{id}").to("bean:foo"); from("direct:start").log(LoggingLevel.DEBUG, "Processing USD{id}").to("bean:foo"); from("file://target/files").log(LoggingLevel.DEBUG, "fileRoute", "Processing file USD{file:name}").to("bean:foo"); <route id="foo"> <from uri="direct:foo"/> <log message="Got USD{body}"/> <to uri="mock:foo"/> </route> <route id="baz"> <from uri="direct:baz"/> <log message="Me Got USD{body}" loggingLevel="FATAL" logName="cool"/> <to uri="mock:baz"/> </route> CamelContext context = context.getProperties().put(Exchange.LOG_EIP_NAME, "com.foo.myapp"); <camelContext ...> <properties> <property key="CamelLogEipName" value="com.foo.myapp"/> </properties> from("direct:start") .to("log:foo") .wireTap("direct:tap") .to("mock:result"); <route> <from uri="direct:start"/> <to uri="log:foo"/> <wireTap uri="direct:tap"/> <to uri="mock:result"/> </route> from("direct:start") .wireTap("direct:foo", new Processor() { public void process(Exchange exchange) throws Exception { exchange.getIn().setHeader("foo", "bar"); } }).to("mock:result"); from("direct:foo").to("mock:foo"); from("direct:start") .wireTap("direct:foo", constant("Bye World")) .to("mock:result"); from("direct:foo").to("mock:foo"); <route> <from uri="direct:start2"/> <wireTap uri="direct:foo" processorRef="myProcessor"/> <to uri="mock:result"/> </route> <route> <from uri="direct:start"/> <wireTap uri="direct:foo"> <body><constant>Bye World</constant></body> </wireTap> <to uri="mock:result"/> </route> from("direct:start") .wireTap("direct:foo", false, new Processor() { public void process(Exchange exchange) throws Exception { exchange.getIn().setBody("Bye World"); exchange.getIn().setHeader("foo", "bar"); } }).to("mock:result"); from("direct:foo").to("mock:foo"); from("direct:start") .wireTap("direct:foo", false, constant("Bye World")) .to("mock:result"); from("direct:foo").to("mock:foo"); <route> <from uri="direct:start2"/> <wireTap uri="direct:foo" processorRef="myProcessor" copy="false"/> <to uri="mock:result"/> </route> <route> <from uri="direct:start"/> <wireTap uri="direct:foo" copy="false"> <body><constant>Bye World</constant></body> </wireTap> <to uri="mock:result"/> </route> from("direct:start") // tap a new message and send it to direct:tap // the new message should be Bye World with 2 headers .wireTap("direct:tap") // create the new tap message body and headers .newExchangeBody(constant("Bye World")) .newExchangeHeader("id", constant(123)) .newExchangeHeader("date", simple("USD{date:now:yyyyMMdd}")) .end() // here we continue routing the original messages .to("mock:result"); // this is the tapped route from("direct:tap") .to("mock:tap"); <route> <from uri="direct:start"/> <!-- tap a new message and send it to direct:tap --> <!-- the new message should be Bye World with 2 headers --> <wireTap uri="direct:tap"> <!-- create the new tap message body and headers --> <body><constant>Bye World</constant></body> <setHeader headerName="id"><constant>123</constant></setHeader> <setHeader headerName="date"><simple>USD{date:now:yyyyMMdd}</simple></setHeader> </wireTap> <!-- here we continue routing the original message --> <to uri="mock:result"/> </route> from("direct:start") .wireTap("jms:queue:backup-USD{header.id}") .to("bean:doSomething");