Performance Monitoring with Spring AOP

Functional requirements for mission critical applications often include software service level agreements (SLAs), which specify the maximum allowable latency for specific operations. Such requirements necessitate the monitoring of application performance, either at system testing stage or post deployment. This article will demonstrate how real-time performance tracing/monitoring can be achieved for spring applications using the functionality provided in Spring AOP and the Apache Log4j toolkit.

Introduction

Assume that as a solution developer/designer, you are given a list of semi-feasible functional requirements which a proposed application has to meet. Whilst reading through the list you parrot the seemingly non-controversial contents back to yourself. That is of course until you reach a line that reads something along the lines of "requests of type ‘y’ have to complete in ‘x’ seconds at the most".

Given that a typical application will have several points of failure which are not within your control as a solution designer, you will be forgiven for going back to your user base and explaining that such a requirement is quite simply not feasible. If you are really unlucky, you may have to go home and start working on yet another revision of your curriculum vitae. But since most users are actually reasonable people, you will probably achieve a compromise along the lines of "if requests of type ‘y’ are taking longer than ‘x’, then perform this action". Suffice it to say that the action to be performed falls under the general heading "details"; but you would have at least reduced your requirement to something a bit more tractable.

This type of functional requirement is no longer uncommon and is often referred to, interchangeably either as a Latency Requirement or Software Service Level Agreement (SLA for short). SLAs generally take the form "operations of type 'y' must complete within 'x' units of time". As an example, the backend for an online stock trading platform might include a restriction of the form "all trades must be submitted to the exchange within 5 seconds of being placed".

The challenges that performance SLAs present to software developers are two-fold. At the design and implementation phase, the emphasis is on ensuring that the application is built in such a way as to encourage fast enough throughput in order to bring the average-case latency in line with the SLA. The second challenge concerns the monitoring of the application's performance in real-time, either at system testing stage or post deployment, so as to detect/predict possible breaches of its SLA.

In this tutorial, we will focus on how to leverage the functionality of the Spring Framework to achieve real-time performance measurement and monitoring. This is a hands-on tutorial, so please download the companion source archive and load the files into your preferred IDE. The source files which comprise the archive are shown in Figure 1.

Figure 1

Note that, in addition to the Spring binaries, the lib folder of the companion archive also contains the binaries for the following open-source dependencies:

Although not strictly necessary, it is advisable to download Apache’s Chainsaw application, which can be used for viewing the logs of remote applications.

For the benefit of the uninitiated, we will begin with a very simple primer on Aspect Oriented Programming (AOP); this will cover the basics needed to follow the examples in this article. For more detailed information, please see the links provided in the resources section. If you are already familiar with AOP, it may well be better to skip the next subsection altogether.

A Primer on AOP

Object Oriented (OO) programming provides for application entities and behaviour to be encapsulated in a set of classes. Although this is quite effective, and would suffice for most application, the truth is that there is certain behaviour in applications that appear everywhere and yet do not really belong anywhere. Examples abound when you look at non-AOP code in detail: the logging of entry and exit trace messages, operation-level authentication and authorization, and transaction control to name but a few.

AOP, or Aspect Oriented Programming to give it its full name, is a means of modularizing and externalizing functionality that spans multiple classes. In AOP terminology such functionality are referred to as cross cutting concerns. If we go back to the example on logging, a logging/error-reporting strategy actually spans multiple classes, and may not really be relevant to the functionality encapsulated by the logged classes. With AOP the log strategy can be externalized as a concern to which classes and methods can be added as and when necessary.

The main concepts from AOP which we are interested in for the purposes of this tutorial are: cross cutting concerns; advice; pointcut; and aspect. The first of these has already been described in the preceding paragraphs; the others are described next.

  • Advice: Encapsulates externalized behaviour which can be applied to class instances at configured points during the execution of an application; for example, the logging of an entry or exit trace message can be thought of as advice types. Advice can also be thought of as the physical code which models/implements the logic of a cross cutting concern.
  • Pointcut: A point in the execution of a program at which cross cutting concerns should be applied. This is the point at which the code encapsulated by an advice is executed.
  • Aspect: An aspect is the combination of an advice and one or more pointcut definitions.

For more information on AOP, please see the resources section of this article. In the next section we will look at a basic service implementation, whose performance we will monitor via Spring AOP.

The Service to Monitor

The service to monitor is defined by the contract interface com.myorg.service.MyService, where the operation being monitored are implementations of the epsilon() method.

The objective of this tutorial is to show how the start and end time of invocations of this method can be intercepted, so as to provide the duration of such invocations.

We will focus on two means of monitoring with Spring AOP, with Spring's own PerformanceMonitorInterceptor and the JamonPerformanceMonitorInterceptor. The latter, although part of the spring distribution, relies on the JAMon application monitoring library. It is worth stating that the full features of JAMon (specifically the monitoring web-based UI) is only really available to web applications, and not standalone Java apps.

Although separate implementations of the service are provided for the Spring and JAMon interceptor examples, this is not a necessity for real world implementations. As you will see, the reason for doing so is merely to ensure clarity of object hierarchies in the tutorial's Spring configuration file.

The first example, covered in the following section, looks at Spring's PerformanceMonitorInterceptor.

Monitoring with Spring's PerformanceMonitorInterceptor

Using Spring's performance monitor is quite simple and can be done without embedding profiling code directly into application classes. To examine how this is done, we first of all need an implementation of the service interface com.myorg.service.MyService; this is provided by the class com.myorg.service.springmon.MyServiceSpringImpl which is shown in Listing 1.

Listing 1: MyServiceSpringImpl


package com.myorg.service.springmon;

import com.myorg.service.MyService;

public class MyServiceSpringImpl implements MyService
{
	protected static final long DEFAULT_SLEEP_INTERVAL = 100;
	private long sleepInterval;

	public MyServiceSpringImpl()
	{
		setSleepInterval(DEFAULT_SLEEP_INTERVAL);
	}	
	
	public void epsilon()
	{
		try{
			Thread.sleep(getSleepInterval());	
		}catch (InterruptedException int_exce){
			throw new RuntimeException("Service interrpted. Exiting.", 
										int_exce);
		}
	}

	public long getSleepInterval()
	{return sleepInterval;}

	public void setSleepInterval(long sleepInterval)
	{this.sleepInterval = sleepInterval;}
}

The implementation of the contract method epsilon() simply causes the thread to sleep for a preset interval. Consequently, we should expect the performance monitor to register a value reasonably close to the sleep interval.

To make use of this service, we have to declare it in the application's Spring configuration file. Not only that, but we also have to indicate that the epsilon() method is a pointcut at which we want monitoring advice executed in any thread of execution cutting across it. The snippet of the configuration to which this relates is shown in Listing 2, which is an excerpt of the application configuration file beans-context.xml.

Listing 2: beans-context.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 		
       	http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
       	http://www.springframework.org/schema/aop 	
       	http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

	<bean id="springMonitoredService"
		class="com.myorg.service.springmon.MyServiceSpringImpl"/>


	<bean id="springMonitoringAspectInterceptor" 		
class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor">
		<property name="loggerName" 	
				value="com.myorg.SPRING_MONITOR"/>		
	</bean>
	
		
	<aop:config>
       		<aop:pointcut id="springMonitoringPointcut"
                   expression="execution(* com.myorg.service..MyServiceSpringImpl.epsilon(..))"/>
                    
       	
                    

        		<aop:advisor pointcut-ref="springMonitoringPointcut" 
			advice-ref="springMonitoringAspectInterceptor"/>      
	</aop:config>
	
</beans>

The configuration file opens with a definition of the service bean, with id springMonitoredService, and then a declaration of the performance monitor springMonitoringAspectInterceptor. The latter of these serves as the advice which we want applied at the invocation of the service's epsilon() method.

Having defined the service, and a representation of the advice, we specify the pointcut at which we want the advice applied. This is done within the <aop:config> element of the bean definition file. This contains a pointcut with id springMonitoringPointcut, which specifies a regular expression that matches all invocations of the epsilon() method in objects of type com.myorg.service..MyServiceSpringImpl. Observe the use of the double period (..) notation in the specification of the class name. This is akin to the pattern com.myorg.service.*.MyServiceSpringImpl. Observe also that the pointcut is actually a bean in itself.

Finally, all we need to complete this example is a definition of the aspect; which, if you recall, is a combination of the pointcut and the advice. This is contained in the element <aop:advisor>, which references both the pointcut and the advice to form an aspect.

Before we move onto the client application, please note that a log4j properties file is included in the config folder of the companion archive. This specifies appenders and layouts that will process the log statements generated by the sample application. At this point you may be wondering where such log messages will come from since we have not actually performed any logging thus far. The answer lies in the logic of the Spring performance monitor, which actually prints task durations via log4j using a TRACE level message.

Observe that the log4j file provided declares a SocketHubAppender, which will allow viewing of log messages with Apache's chainsaw. The use of chainsaw is not strictly necessary for this example, but is provided simply as a means of demonstrating how a complete solution can be setup in the real world. This is more relevant for the JAMon example as we do not have recourse to the JAMon GUI--it is only really applicable to web applications.

All that is left to do is to provide a client for invoking the service. A single client application is used for this tutorial; however, a different method is provided for invoking each example. As such, some light and judicious use of Java's commenting construct will be required to exclude the examples that you do not want to run. Listing 3 shows the main contents of the example client com.myorg.MyClientApp.

Listing 3: MyClientApp

1 2 Page 1
Page 1 of 2