1. Introduction
Monitoring is very helpful for finding bugs and optimizing performance. We could manually instrument our code to add timers and logging, but this would lead to a lot of distracting boilerplate.
On the other hand, we can use a monitoring framework, driven by annotations, such as Dropwizard Metrics.
In this tutorial, we will instrument a simple class using Metrics AspectJ, and the Dropwizard Metrics @Timed annotation.
2. Maven Setup
First of all, let’s add the Metrics AspectJ Maven dependencies to our project:
<dependency>
<groupId>io.astefanutti.metrics.aspectj</groupId>
<artifactId>metrics-aspectj</artifactId>
<version>1.2.0</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.astefanutti.metrics.aspectj</groupId>
<artifactId>metrics-aspectj-deps</artifactId>
<version>1.2.0</version>
</dependency>
We’re using metrics-aspectj to provide metrics via aspect oriented programming, and metrics-aspectj-deps to provide its dependencies.
We also need the aspectj-maven-plugin to set up compile time processing of the metrics annotations:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.8</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
<aspectLibraries>
<aspectLibrary>
<groupId>io.astefanutti.metrics.aspectj</groupId>
<artifactId>metrics-aspectj</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
Now our project is ready to have some Java code instrumented.
3. Annotation Instrumentation
Firstly, let’s create a method and annotate it with the @Timed annotation. We’ll also fill the name property with a name for our timer:
import com.codahale.metrics.annotation.Timed;
import io.astefanutti.metrics.aspectj.Metrics;
@Metrics(registry = "objectRunnerRegistryName")
public class ObjectRunner {
@Timed(name = "timerName")
public void run() throws InterruptedException {
Thread.sleep(1000L);
}
}
We’re using the @Metrics annotation at the class level to let the Metrics AspectJ framework know this class has methods to be monitored. We’re putting @Timed on the method to create the timer.
In addition, @Metrics creates a registry using the registry name provided – objectRunnerRegistryName in this case – to store the metrics.
Our example code just sleeps for one second to emulate an operation.
Now, let’s define a class to start the application and configure our MetricsRegistry:
public class ApplicationMain {
static final MetricRegistry registry = new MetricRegistry();
public static void main(String args[]) throws InterruptedException {
startReport();
ObjectRunner runner = new ObjectRunner();
for (int i = 0; i < 5; i++) {
runner.run();
}
Thread.sleep(3000L);
}
static void startReport() {
SharedMetricRegistries.add("objectRunnerRegistryName", registry);
ConsoleReporter reporter = ConsoleReporter.forRegistry(registry)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.outputTo(new PrintStream(System.out))
.build();
reporter.start(3, TimeUnit.SECONDS);
}
}
In the startReport method of ApplicationMain, we set up the registry instance to the SharedMetricRegistries using the same registry name as used in @Metrics.
After that, we create a simple ConsoleReporter to report our metrics from the @Timed annotated method. We should note that there are other types of reporters available.
Our application will call the timed method five times. Let’s compile it with Maven and then execute it:
-- Timers ----------------------------------------------------------------------
ObjectRunner.timerName
count = 5
mean rate = 0.86 calls/second
1-minute rate = 0.80 calls/second
5-minute rate = 0.80 calls/second
15-minute rate = 0.80 calls/second
min = 1000.49 milliseconds
max = 1003.00 milliseconds
mean = 1001.03 milliseconds
stddev = 1.10 milliseconds
median = 1000.54 milliseconds
75% <= 1001.81 milliseconds
95% <= 1003.00 milliseconds
98% <= 1003.00 milliseconds
99% <= 1003.00 milliseconds
99.9% <= 1003.00 milliseconds
As we can see, the Metrics framework provides us with detailed statistics for very little code change to a method we want to instrument.
We should note that running the application without the Maven build – for example, through an IDE – might not get the above output. We need to ensure the AspectJ compilation plugin is included in the build for this to work.
4. Conclusion
In this tutorial, we investigated how to instrument a simple Java application with Metrics AspectJ.
We found Metrics AspectJ annotations a good way to instrument code without needing a large application framework like Spring, JEE, or Dropwizard. Instead, by using aspects, we were able to add interceptors at compile-time.
As always, the complete source code for the example is available over on GitHub.