A MonitorProcessor is a class that consumes Monitors created by the application in order to do something useful with the data. What exactly a processor can do with a Monitor is limitless. A MonitorProcessor could log a Monitor to log4j, convert the Monitor events into events in another monitoring system or even provide aggregation services. It might even mutate a given Monitor to add, update or delete attributes before processing by another processor.
How do I make a MonitorProcessor?
Making a MonitorProcessor is as simple as implementing the MonitorProcessor interface. The interface has two groups of methods: lifecycle methods and monitor processing methods.
These are methods that will get called to inform your processor of major events in the ERMA usage.
The startup() method will be called when MonitoringEngine.startup() is called. All your JavaBean properties will have been configured. You should allocate any resources you will need for processing in this method. For example, if you will be logging some data to a file, you should create and open that file here.
The shutdown() method will be called when MonitoringEngine.shutdown() is called (surprise\!). You should use this opportunity to flush any pending data, close any resources, and generally prepare for death.
Yay, I opened a file. Now what?
You'll want to put some useful code in the monitor processing methods. At this time, there are three such methods.
public void monitorCreated(Monitor monitor)
This is a method that is called whenever a monitor is created. No matter what the type, all Monitor implementations will call this method. You can use this method for any purpose that makes sense in this context. One such use might be to set a certain attribute on a monitor.
public void monitorStarted(Monitor monitor)
This method is called whenever a monitor that wraps a unit of work is "started." For example, the TransactionMonitor implementation calls this method at object creation because that is when it's clock starts. Again, anything is game here. It's important to realize that this method is called only by those implementations that wrap a unit of work: non-"unit of work" monitors such as EventMonitor do not call this method.
public void process(Monitor monitor)
This method is the most commonly used method. This method is called when a Monitor is finished being used by the application. At this point, all attributes that will ever be set have been set on the Monitor. You can use this method for any purpose. Some common uses might be to render the monitor to a file, transmit it to a remote monitoring application, even update some aggregate calculation such as the sum of all monitors of a certain type received over the last minute.
There's just one "tiny" thing to consider…
Monitoring an application often involves receiving and processing multiple Monitors being generated from multiple threads simultaneously. When creating your MonitorProcessor you should take care to ensure that your implementation is thread safe and quick. By default, a Monitor is processed on the *same thread* as the user's request. This means any latency incurred by your monitor processor directly affects the user wait time. Also, your implementation is a shared resource among multiple threads. You need to ensure that it will behave correctly when hundreds of threads are calling methods on your processor. The JUnitPerf library is invaluable for stress testing your MonitorProcessor implementation.
If your implementation simply cannot be quick enough to support processing hundreds of monitor callbacks at a nearly unprofilable rate, you should clearly document this aspect. A developer who uses your implementation should then use an intermediary processor that will cause the processing to occur on a separate thread. The AsyncMonitorProcessor has been created for this case. If this processor sits between you and the user thread, all processing will occur on a separate thread. This effectively decouples the user's thread from your processing code. It also has the benefit of removing the thread-safe requirement from your implementation since now all callbacks will come from a single background thread.