There are numerous logging and monitoring options available in Apache Camel. The most obvious being the Log component and Log EIP.
http://camel.apache.org/log.html
http://camel.apache.org/logeip.html
However, a sometimes more overlooked option is the Camel Eventnotifier. The Eventnotifier can listen on various events sent by a Camel context. The eventnotifier will catch the Camel Exchanges puched by these events. So do note the eventnotifier is logging on a Exchange level. In this post I will explain an example of the Camel eventnotifier.
We are going to start with the default example which comes with the Blueprint archetype in JBoss Fuse.
As you can see in this example the log EIP is used. The log EIP in this example is configured like this:
When we run this example we see the following output in the log:
[ntext) thread #0 - timer://foo] timerToLog INFO The message contains Hi from Camel at 2014-10-10 13:37:27 [ntext) thread #0 - timer://foo] timerToLog INFO The message contains Hi from Camel at 2014-10-10 13:37:32 [ntext) thread #0 - timer://foo] timerToLog INFO The message contains Hi from Camel at 2014-10-10 13:37:37 [ntext) thread #0 - timer://foo] timerToLog INFO The message contains Hi from Camel at 2014-10-10 13:37:42
In this post we are going to recreate this logging output but instead of using the log EIP we are going to use a Camel Eventnotifier. After this I will show you how to extend the logging of the Camel Context further without making any changes to the route. It is this concept which makes the Eventnotifier very powerfull.
The first step is to create the Eventnotifier class.
Creating a custom EventNotifier class
Create a new Java class extending org.apache.camel.support.EventNotifierSupport (note there is also an EventNotifierSupport class in the management package but this one is deprecated).
We are going to implement two inherited methods:
isEnabled – here you can configure what types of events the eventnotifier can subscribe to
notify – this is the method who gets called when the event occures. Here you would implement the logic to handle the event.
In the isEnabled method we are going to enable the ExchangeSentEvent. This event is triggered after an exchange has been sent to and endpoint.
When this event occures the notify method is called with an abstract event object as the parameter. So in the notify method we need to first cast this extract event to the ExchangeSentEvent object so we can use this specialized object. Then we have to extract the Camel Exchange from this event, extract the body part of the in message (we use the in message since we are sending to a fire and forget style endpoint and want to have the message we sent to this endpoint).
The notify method looks like this:
// We are going to extract first the Camel Exchange from the event and after this extract the body from the Exchange ExchangeSentEvent sentEvent = (ExchangeSentEvent) event; Exchange exchange = sentEvent.getExchange(); String msgBody = (String) exchange.getIn().getBody(); log.info("The message contains " + msgBody);
The entire Java Class of our eventnotifier looks like this:
package nl.rubix.notifier.example.notifier; import java.util.EventObject; import org.apache.camel.Exchange; import org.apache.camel.management.event.ExchangeSentEvent; import org.apache.camel.support.EventNotifierSupport; public class MyEventnotifier extends EventNotifierSupport{ @Override public boolean isEnabled(EventObject event) { // We are going to enable the ExchangeSentEvent this event will be published after an exchange has been sent to an endpoint in the Camel route return event instanceof ExchangeSentEvent; } @Override public void notify(EventObject event) throws Exception { if(event instanceof ExchangeSentEvent){ // We are going to extract first the Camel Exchange from the event and after this extract the body from the Exchange ExchangeSentEvent sentEvent = (ExchangeSentEvent) event; Exchange exchange = sentEvent.getExchange(); String msgBody = (String) exchange.getIn().getBody(); log.info("The message contains " + msgBody); } } }
Next we have to instantiate the MyEventnotifier class as a bean in the Camel context, just you would instantiate any other beans.
<bean id="myEventNotifier" class="nl.rubix.notifier.example.notifier.MyEventnotifier"/>
When we run this example we see the following output in the log (note: we didn’t remove the log EIP in the Camel route):
[ntext) thread #0 - timer://foo] timerToLog INFO The message contains Hi from Camel at 2014-10-10 14:32:46 [ntext) thread #0 - timer://foo] MyEventnotifier INFO The message contains Hi from Camel at 2014-10-10 14:32:46 [ntext) thread #0 - timer://foo] timerToLog INFO The message contains Hi from Camel at 2014-10-10 14:32:51 [ntext) thread #0 - timer://foo] MyEventnotifier INFO The message contains Hi from Camel at 2014-10-10 14:32:51
Extending the example
As you can see the log entry of the EventNotifier contains MyEventnotifier in stead of timerToLog. In this case we have only one Camel Route so it is not a big deal the name of the route does not show up in the log. But what if we have multiple routes, in this case it would definitely be nice to know what route triggered the ExchangeSentEvent. Luckilly we can easily get the route name from the exchange using the ‘getFromRouteId()’ available on the Exchange object.
So when we extend our notify class with the route name will be logged:
String routeName = exchange.getFromRouteId(); log.info("The message contains " + msgBody + " route: " + routeName);
The log entry now looks like this:
[ntext) thread #0 - timer://foo] MyEventnotifier INFO The message contains Hi from Camel at 2014-10-10 14:38:58 route: timerToLog
But what if we want to use other types of events as well?
Extending the EventNotifier to use other events
First we need to enable the other types of events we want to receive in the isEnabled method. This can be done by simply adding them to the return statement:
return event instanceof ExchangeSentEvent || event instanceof CamelContextStartedEvent;
Next we need to implement some logic for handling the CamelContextStartedEvent in the notify mehtod. In this case we want to show the name of the Camel Context when it has been started. To do this simply add the following to the notify method:
else if(event instanceof CamelContextStartedEvent){ CamelContextStartedEvent contextCreatedEvent = (CamelContextStartedEvent) event; String contextName = contextCreatedEvent.getContext().getName(); log.info("Camel Context: " + contextName + " started"); }
Now when we run the example we see the following entry in the log:
[ Blueprint Extender: 1] MyEventnotifier INFO Camel Context: blueprintContext started
The power of the EventNotifier lies in the fact you can extend the logging capabilities of Camel without changing individual routes and Contexts. Simply define your EventNotifier and instantiate it once in the CamelContexts you want to monitor. Another example of this is that all your Camel Contexts and Routes will have a standardised way of logging and log moments.
The final MyEventnotifier class looks like this:
package nl.rubix.notifier.example.notifier; import java.util.EventObject; import org.apache.camel.Exchange; import org.apache.camel.management.event.ExchangeSentEvent; import org.apache.camel.support.EventNotifierSupport; import org.apache.camel.management.event.CamelContextStartedEvent; public class MyEventnotifier extends EventNotifierSupport{ @Override public boolean isEnabled(EventObject event) { // We are going to enable the ExchangeSentEvent this event will be published after an exchange has been sent to an endpoint in the Camel route return event instanceof ExchangeSentEvent || event instanceof CamelContextStartedEvent; } @Override public void notify(EventObject event) throws Exception { if(event instanceof ExchangeSentEvent){ // We are going to extract first the Camel Exchange from the event and after this extract the body from the Exchange ExchangeSentEvent sentEvent = (ExchangeSentEvent) event; Exchange exchange = sentEvent.getExchange(); String msgBody = (String) exchange.getIn().getBody(); String routeName = exchange.getFromRouteId(); log.info("The message contains " + msgBody + " route: " + routeName); } else if(event instanceof CamelContextStartedEvent){ CamelContextStartedEvent contextCreatedEvent = (CamelContextStartedEvent) event; String contextName = contextCreatedEvent.getContext().getName(); log.info("Camel Context: " + contextName + " started"); } } }