Monday, September 13, 2010

Spring AOP Framework – Method Logging


Spring AOP Framework – Method Logging

Aspect oriented programming (AOP) enables you to write code for a crosscutting functionality and then apply it declaratively to existing code. Logging is a prime candidate for implementation as an aspect, and Spring offers the AOP framework to get it done.

Spring AOP is implemented in pure java and only supports method execution join points. Spring AOP implementation uses a standard AOP API from the AOP Alliance. Spring AOP framework provides a close integration with Spring IOC container, aspects are configured using usual bean definition syntax, and uses Java dynamic proxies for AOP proxies, enabling any interface or set of interfaces to be proxied. Spring also supports integration with other popular AOP frameworks like AspectJ.
Spring's 'built-in' AOP infrastructure is defined by the org.springframework.aop.* packages.
  • Aspect - Think of this as the general feature you want to apply globally to your application (logging, performance monitoring, exception handling, transaction management, etc).
  • Advice - A chunk of code that is invoked during program execution, and is a piece of the logic for implementing your aspect. This is the first important piece of a Spring AOP aspect implementation! I like to compare advice implementations to the decorator pattern. While an advice doesn't necessarily wrap an entire object in concept, it has the same general effect. We'll learn in a bit that how that advice is applied is more granular/formal than typically defined in the decorator pattern however.
  • Joinpoint - A *single* location in the code where an advice should be executed (such as field access, method invocation , constructor invocation, etc.). Spring's built-in AOP only supports method invocation currently, so joinpoints aren't particularly important to focus on at this point.
  • Pointcut - A pointcut is a set of many joinpoints where an advice should be executed. So if, in Spring, a joinpoint is always a method invocation, then a pointcut is just a set of methods that, when called, should have advices invoked around them. This is the second important pieces of a Spring AOP aspect implementation!
  • Targets/Target Objects - The objects you want to apply an aspect or set of aspects to!
**The following example shows configuration of method level logging with spring AOP.

MethodLoggingInterceptor.java

package logging;

import java.util.Arrays;
import java.util.HashMap;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.log4j.Logger;

public class MethodLoggingInterceptor implements MethodInterceptor {

  private final Logger log = Logger.getLogger(getClass());
  private HashMap<Class, Logger> loggers = new HashMap<Class, Logger>();
  private boolean printStacktrace = true;

  public boolean isPrintStacktrace() {
    return printStacktrace;
  }

  public void setPrintStacktrace(boolean printStacktrace) {
    this.printStacktrace = printStacktrace;
  }

  /* (non-Javadoc)
   * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
   */
  public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
    log.info("Entering MethodLoggingInterceptor.invoke");
    if (log.isTraceEnabled()) {
      log.trace("parameter methodInvocation=" + methodInvocation);
    }
    Class declaringClass = methodInvocation.getMethod().getDeclaringClass();
    Logger logger = this.loggers.get(declaringClass);
    if (logger == null) {
      logger = Logger.getLogger(declaringClass);
      this.loggers.put(declaringClass, logger);
    }

    String targetMethodSignature = methodInvocation.getMethod().toString();
    try {
      logger.info("Entering " + targetMethodSignature);
      if (logger.isDebugEnabled()) {
        logger.debug("parameters are:" + Arrays.asList(methodInvocation.getArguments()));
      }
      Object retVal = methodInvocation.proceed();
      logger.info("Exiting " + targetMethodSignature);
      if (logger.isDebugEnabled()) {
        logger.debug("return value = " + retVal);
      }
      return retVal;
    } catch (Throwable e) {
      logger.error("Error while calling " + targetMethodSignature);
      if (this.isPrintStacktrace()) {
        logger.error("", e);
      }
      log.info("Exiting MethodLoggingInterceptor.invoke");
      throw e;
    } finally {
      log.info("Exiting MethodLoggingInterceptor.invoke");
    }
  }

}

** We can also implement the aspects more specifically using 
MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice.

import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;
 
public class MethodLoggingInterceptor implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice{
        private static Log log = null;
        public LoggingInterceptor(){
        }
        
        public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
               log = LogFactory.getLog(arg2.getClass());
               log.info("Beginning method: "+arg0.getName());
        }
        
        public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {             
               log = LogFactory.getLog(arg3.getClass());
               log.info("Ending method: "+arg1.getName());           
        }
        
        public void afterThrowing(Method m, Object[] args, Object target, Throwable ex) 
        { 
               log = LogFactory.getLog(target.getClass());
               log.info("Exception in method: "+m.getName()+" Exception is: "+ex.getMessage());    
        }
 
}

AOP configuration in applicationContext.xml

      <aop:config>
            <aop:pointcut id="getAllMethodsPointcut"
                  expression="within(data..*)" />
            <aop:advisor id="loggingAdvisor"
                  advice-ref="loggingAdvice" pointcut-ref="getAllMethodsPointcut" />
      </aop:config>
 
      <bean id="loggingAdvice" class="logging.MethodLoggingInterceptor">
            <property name="printStacktrace" value="false"></property>
      </bean>
            <bean id="log4jInitialization" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
            <property name="targetClass" value="org.springframework.util.Log4jConfigurer" />
            <property name="targetMethod" value="initLogging" />
            <property name="arguments">
                  <list>
                        <value>classpath:userservice.log4j.properties</value>
                  </list>
            </property>
      </bean>