... the ways to monitor the classes of your application with MoSKito Monitoring.
AOP
To integrate MoSKito into your application and monitor your data and code with AOP, follow two simple steps.
Step 1: Annotate classes and methods
For classes, add @Monitor annotation.
@Monitor public class YourClass {
For methods, add the same @Monitor annotation.
public class YourClass { @Monitor public void firstMonitoredMethod(){... @Monitor public void secondMonitoredMethod(){... public void notMonitoredMethod(){...
To monitor a class but exclude some of its methods:
- Add @Monitor annotation to the target class.
- Add @DontMonitor annotation to the methods you want to skip.
@Monitor public class YourClass { public void thisMethodWillBeMonitored(){... @DontMonitor public void thisMethodWillBeExcludedFromMonitoring(){...
The same workflow applies to Counters.
@Count public class PaymentCounter {
You can also specify a producerId, subsystem or category to monitor:
@Monitor(producerId="MyProducer", subsystem="mySub", category="myCategory") public class YourClass {
Step 2: Alter pom.xml
After annotating the classes for monitoring, tell the compiler to actually weave them. In Maven, this is done by adding the following two blocks to your pom.xml:
<dependencies> <dependency> <groupId>net.anotheria</groupId> <artifactId>moskito-core</artifactId> <version>...</version> </dependency> <dependency> <groupId>net.anotheria</groupId> <artifactId>moskito-aop</artifactId> <version>...</version> </dependency>
and now the build section:
<build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>1.4</version> <configuration> <aspectLibraries> <aspectLibrary> <groupId>net.anotheria</groupId> <artifactId>moskito-aop</artifactId> </aspectLibrary> </aspectLibraries> <source>1.6</source> <target>1.6</target> </configuration> <executions> <execution> <goals> <goal>compile</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
This is it.
For a quick reference, see the aop module of moskito-examples GitHub project.
Also, read the reference for all MoSKito-AOP features in Working with Annotations guide.
CDI
The CDI integration is pretty similar to the AOP integration, but has differences. Anyway, CDI also involves two simple steps.
Step 1: Annotate classes and methods
For classes, add @Monitor annotation (in the same way with AOP).
@Monitor public class YourClass {
You may bind an annotation with a special value, later used to map a specific interceptor.
For example, we provide DAO, SERVICE or WEB interceptors, which automatically put the registered producers into specific categories.
@Monitor("service") public class YourClass { @Monitor("dao") public class AnotherClass { @Monitor("whatever") public class YetAnotherClass {
For methods, use the same @Monitor annotation.
public class YourClass { @Monitor public void firstMonitoredMethod(){... @Monitor public void secondMonitoredMethod(){... public void notMonitoredMethod(){...
Step 2: Add interceptors to beans.xml and to the project
To activate MoSKito interceptor (either your own or built-in) for CDI, add it to beans.xml:
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd"> <interceptors> <class>net.anotheria.moskito.integration.cdi.CountInterceptor</class> <class>net.anotheria.moskito.integration.cdi.CallInterceptor</class> <!-- Customized interceptors for @Monitor with parameters --> <class>net.anotheria.moskito.integration.cdi.WebCallInterceptor<class> <class>net.anotheria.moskito.integration.cdi.ServiceCallInterceptor</class> <class>net.anotheria.moskito.integration.cdi.DaoCallInterceptor</class> <!-- You may add your interceptor too --> <class>com.company.project.moskitointegration.MobileCallInterceptor</class> </interceptors> </beans>
For additional info and examples, see CDI Integration examples in MoSKito-JBoss project on GitHub.
WEB
The WEB Integration is divided into several subparts.
Filters
MoSKito comes with a set of built-in filters for different counting purposes. All the following filters are monitoring the same stat type, FilterStats. The difference is the extraction of the stat name itself. For example, RequestURIFilter separates calls by URL and the DomainFilter - by called server name.
Below are the filters in version 2.2.3:
Filter | Purpose |
---|---|
net.anotheria.moskito.web.filters.AsyncSourceTldFilter | Separates traffic by top level source domain. This Filter is more secure than SourceTldFilter because it: a) performs the lookup asynchronously, without support from container, and b) is invulnerable to DNS attacks, for the same reason as a). |
net.anotheria.moskito.web.filters.DomainFilter | Separates traffic by called server name. |
net.anotheria.moskito.web.filters.MethodFilter | Separates traffic by HTTP Method (GET, POST, PUT). |
net.anotheria.moskito.web.filters.RefererFilter | Separates traffic by referrer (if any). |
net.anotheria.moskito.web.filters.RequestURIFilter | Separates traffic by URL, in other words, you get request count, time, etc., for each callable URL in your system. |
net.anotheria.moskito.web.filters.SourceIpSegmentFilter | Separates traffic by the first byte of IP4 Address. |
net.anotheria.moskito.web.filters.SourceTldFilter | Separates traffic by top level client domain. This filter relies on container's resolving, which is switched off by default on most containers. |
net.anotheria.moskito.web.filters.UserAgentFilter | Separates traffic by User Agent. Unfortunately, this filter uses the whole user-agent header as stat name and not just browser. The latter would be great, we are looking for contributions |
For all the above filters the integration is done via web.xml:
<filter> <filter-name>RequestURIFilter</filter-name> <filter-class>net.anotheria.moskito.web.filters.RequestURIFilter</filter-class> <init-param> <param-name>limit</param-name> <param-value>1000</param-value> </init-param> </filter> <filter-mapping> <filter-name>RequestURIFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
The limit parameter is used to prevent memory overload.
Since the number of stats is mostly unknown to MoSKito, it adds new entries on the fly. However, since every URL, called once, will be monitored, it would be easy to attack a monitored site by simply calling non-existing URLs.
To prevent this attack, a limit on supported stat name size is applied. 1000 should be a good limit for most production sites.
Custom filters
All the above filters (or almost all) are built with the same principle:
- Extend net.anotheria.moskito.web.MoskitoFilter,
- Implement protected abstract String extractCaseName(ServletRequest req, ServletResponse res ).
For example, the DomainFilter:
package net.anotheria.moskito.web.filters; import net.anotheria.moskito.web.MoskitoFilter; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; /** * This filter counts requests by domain. * @author lrosenberg * */ public class DomainFilter extends MoskitoFilter { @Override protected String extractCaseName(ServletRequest req, ServletResponse res) { return req.getServerName(); } }
Feel free to create your own
Listeners
MoSKito comes with 2 built-in session listeners: net.anotheria.moskito.web.session.SessionByTldListener and net.anotheria.moskito.web.session.SessionCountProducer. SessionCountProducer simply counts all sessions. SessionByTldListener (which is also a producer, sorry for naming inconvenience) does the same that SessionCountProducer does, but additionally it counts by client's TLD.
So, you would be able to monitor how many of your users are coming from China, from France, from Canada and so on.
Both are integrated via web.xml:
<listener> <listener-class> net.anotheria.moskito.web.session.SessionCountProducer </listener-class> </listener> <listener> <listener-class> net.anotheria.moskito.web.session.SessionByTldListener </listener-class> </listener>
Both are counting new sessions, current sessions, deleted sessions and min/max active sessions, from start and within an interval.
Servlets
There is an easy way to integrate a servlet, by simply extending MoskitoHttpServlet and implementing doGet instead of get.
PROXIES
java.lang.Proxy - proxies are excellent to create monitoring proxies around implementations you want to monitor. MoSKito provides a special ProxyUtils class, that allows to create MonitoringProxies in a quick and easy way.
Let;s assume you created a service:
public interface SimpleService{ void doSomethingMethod(); }
and an implementation:
public class SimpleServiceImpl implements SimpleService{ public void doSomethingMethod(){ } }
All you need to do is to wrap your implementation with a proxy and you are done:
SimpleService service = ProxyUtils.createServiceInstance(new SimpleServiceImpl(), "default", SimpleService.class);
This method is a shortcut method that hunts a lot of implementation details from you. The three parameters are:
Parameter | Meaning |
---|---|
new SimpleServiceImpl() | The instance to be monitored. |
"default" | Subsystem. All producers have a subsystem, a category and a producer name/id. The category is guessed from method's name - "service", the producer id will be generated out of the class name. Subsystem remains. |
SimpleService.class | The interface that should be monitored. You can specify multiple (varargs). Only methods in the specified interfaces will be monitored. If you call toString() in the example above, |
The above call creates a new monitored instance. We strongly advice you to create more than one monitoring instances of your classes, allowing you to separate traffic by source. However, creating a new monitoring instance on each call is counterproductive and will lead to a memory leak.
More ProxyUtils methods:
/** * Creates a new proxied instance for an existing implementation. * @param <T> interface type. * @param impl the implementation of the interface. * @param name name of the producer. * @param category category of the producer, i.e. service, dao, api, controller. * @param subsystem subsystem of the producer, i.e. messaging, payment, registration, shop. * @param handler handler for the calls. * @param statsFactory the factory for the stats. * @param interf interfaces. * @return */ public static <T> T createInstance(T impl, String name, String category, String subsystem, IOnDemandCallHandler handler, IOnDemandStatsFactory statsFactory, Class<T> interf, Class<?>... additionalInterfaces){ /** * Creates a monitored proxy instance for a service. Service in this context means, that the ServiceStatsCallHandler and ServiceStatsFactory are used. * @param <T> the server interface. * @param impl the implementation of T. * @param name name for this instance. * @param category category of this instance. * @param subsystem subsystem of this instance. * @param interf class of T, main interface of the service. * @param additionalInterfaces additional helper interfaces, that should be supported as well. * @return */ public static <T> T createServiceInstance(T impl, String name, String category, String subsystem, Class<T> interf, Class<?>... additionalInterfaces){ /** * Shortcut method to create service instance. Creates an instance with service interface name as instance name, custom category and subsystem, ServiceStatsCallHandler and ServiceStatsFactory. * @param <T> * @param impl * @param category * @param subsystem * @param interf * @param additionalInterfaces * @return */ public static <T> T createServiceInstance(T impl, String category, String subsystem, Class<T> interf, Class<?>... additionalInterfaces){ /** * Shortcut method to create service instance with least possible effort. Creates an instance with service interface name as instance name, category service, ServiceStatsCallHandler and ServiceStatsFactory. * @param <T> service interface. * @param impl implementation of T. * @param subsystem subsystem of the service. * @param interf Class of T. * @param additionalInterfaces Additional interfaces if applicable. * @return */ public static <T> T createServiceInstance(T impl, String subsystem, Class<T> interf, Class<?>... additionalInterfaces){ public static <T> T createDAOInstance(T impl, String subsystem, Class<T> interf, Class<?>... additionalInterfaces){
Note on ProxyUtils
The following pattern is used to name the created producers:
CLASSNAME + "-" + instanceCounter (starting with 1)
In the above example, the ProducerId would be SimpleService-1, not SimpleService. This pattern is a part of duplicate prevention mechanism that doesn't allow producers to be overwritten.
However, the ProxyUtils themselves are only a utility class on top of the MoskitoInvokationProxy (net.anotheria.moskito.core.dynamic.MoskitoInvokationProxy). Although you'll probably never need to use it directly, it should be named here:
SimpleService unmonitoredInstance = new SimpleServiceImpl(); MoskitoInvokationProxy proxy = new MoskitoInvokationProxy( unmonitoredInstance, new ServiceStatsCallHandler(), new ServiceStatsFactory(), "SimpleService", "service", "test-sub-system", SimpleService.class ); SimpleService monitoredInstance = (SimpleService)proxy.createProxy();
Small explanation:
Parameter | Meaning |
---|---|
unmonitoredInstance | The implementation instance of the interface that we want to monitor. |
new ServiceStatsCallHandler() | The handler for calls on the proxy. It handles all the monitoring and accounting. |
new ServiceStatsFactory() | The IOnDemandStatsFactory for Stats. |
"SimpleService" | ProducerId. |
"service" | Category. |
"test-sub-system" | Subsystem. |
SimpleService.class | Monitored interfaces (in this case one). |
For a quick reference, see the dynamicproxy module of moskito-examples GitHub project.
Differences between AOP/CDI and Proxy style monitoring
AOP/CDI | Proxy | |
---|---|---|
Monitors | ... implementation. | ... caller. The Caller of the method goes intercepted, the target doesn't know it's being monitored. |
Methods | ALL. If you call an internal method in a loop and it doesn't do much relevant stuff, exclude it. | Only interface methods. It makes monitoring clearer, because you are usually interested in monitoring |
Limitations | - | Can only monitor interfaces. |
Performance | Faster with compile time weaving. | A bit slower. |
Use when... | ... you want to monitor a special class or special instance, or simple don't want to build proxies. | ... you have a lot of classes of a same category that pass the same code area (factories). This makes proxy creation very easy and effective. |
GOOD OLD COUNTING
Info will be added soon.
BLUEPRINT
Info will be added soon.
CALL EXECUTION
CallExecution is another way of telling the system when to start monitoring.
With AOP or Proxy integration, a Java method becomes a natural boundary of the start and end points of monitored period.
However, there might be situations in which only a part of a method is relevant, or even multiple critical sections in one method. This is where CallExecution comes in handy.
The first, very simple example, is where we try to monitor a part of the method. Of course, we still need a producer:
producer = new OnDemandStatsProducer<ServiceStats>("complexprocess", "category", "subsystem", ServiceStatsFactory.DEFAULT_INSTANCE); ProducerRegistryFactory.getProducerRegistryInstance().registerProducer(producer);
Now we have our important method, in which we want to monitor a critical section:
public void methodWhichIsPartiallyMonitored(){ //doing something //... bla boo bla //here comes the monitored part try{ CallExecution execution = producer.getStats("methodWhichIsPartiallyMonitored").createCallExecution(); execution.startExecution(); //now we are doing something extremely important... execution.finishExecution(); }catch(OnDemandStatsProducerException e){ //react somehow, preferably smarter than the following line: throw new AssertionError("This should never happen in the example environment"); } //doing something else //boo bla boo }
The main difference is that we set the monitoring boundaries manually, instead of settings them automatically with Java method boundaries.
The code below gives a clearer example of monitoring multiple areas in one method:
public void methodWithMultipleComplexSubprocesses(){ //doing something //... bla boo bla //here comes the beginning of our complex process try{ CallExecution execution = producer.getStats("phase1").createCallExecution(); execution.startExecution(); //now we are doing something extremely important... execution.finishExecution(); //now we are doing phase 2. For whatever reasons we have to do it in a loop or something. for (int i=0; i<3; i++){ execution = producer.getStats("phase2").createCallExecution(); execution.startExecution(); //now we are doing something extremely important... execution.finishExecution(); } //no we do something else, until we finally have to do the last phase twice... execution = producer.getStats("phase3").createCallExecution(); execution.startExecution(); //now we are doing something extremely important... execution.finishExecution(); execution = producer.getStats("phase3").createCallExecution(); execution.startExecution(); //now we are doing something extremely important... execution.finishExecution(); //now we are all set }catch(OnDemandStatsProducerException e){ //react somehow, preferably smarter than the following line: throw new AssertionError("This should never happen in the example environment"); } //doing something else // boo bla boo }
This is it.
For a quick reference, see the callexecution module of moskito-examples GitHub project.
1 Comment
Enoque Duarte
After following the guide, adding some \@Monitor to classes, i get the onDemandProducers appearing on Moskito Inspect, but they dont count anything!, everything with 0.