Thought Forge
  • Home
  • Projects
  • The Team

Creating a Logging Aspect with Spring AOP and AspectJ

Feb 16, 2010 ~ 19 Comments ~ Written by John Turner

After working in software development for a number of years, one tends to build up a repository of useful code examples and utilities. I’m certainly no different in this regard and go one step further by maintaining a hosted Subversion repository to keep such code examples safe and sound (and readily accessible).

One such code example that I often turn to is a logging aspect implemented using Spring AOP and AspectJ. This logging aspect traces method entry and exit which proves very useful if you need to perform root cause analysis in pre-production environments.

In what follows, I’m going to share this code example. I have also made the logging aspect source code  available for download.

I have assumed you have a reasonable grasp of Aspect Orientated Programming concepts and terminologies.

The Logger

There are a few options to choose from when considering a logging framework and these have evolved over the years so it is probably best to abstract the logging framework from the aspect. For this purpose, I have implemented a simple logging interface and log level enum.

package net.thoughtforge.logger;

public interface Logger {

  boolean isLogLevel(LogLevel logLevel, Class clazz);

  void log(LogLevel logLevel, Class clazz, Throwable throwable, String pattern, Object... arguments);
}
package net.thoughtforge.logger;

public enum LogLevel {
  DEBUG,
  ERROR,
  FATAL,
  INFO,
  TRACE,
  WARN
}

In the majority of cases, I end up using the Commons Logging framework and so have included this implementation of the Logger in the source download.

You may decide that this level of abstraction is overkill and that you are willing to commit to using a specific logging framework. When making this decision, keep in mind that you will be specifying the log level throughout your code base.

The Logging Aspect

I am going to use the @Aspect approach to implement the logging aspect which is to say I will use annotations to specify the advice. I want the logging aspect to log the method name, argument values, return value and any exception thrown so I will use the @Before, @AfterThrowing and @AfterReturning annotations.

@Before(value = "@annotation(loggable)", argNames = "joinPoint, loggable")
public void before(JoinPoint joinPoint, Loggable loggable) {

  Class clazz = joinPoint.getTarget().getClass();
  String name = joinPoint.getSignature().getName();

  if (ArrayUtils.isEmpty(joinPoint.getArgs())) {
    logger.log(loggable.value(), clazz, null, BEFORE_STRING, name, constructArgumentsString(clazz, joinPoint.getArgs()));
  } else {
    logger.log(loggable.value(), clazz, null, BEFORE_WITH_PARAMS_STRING, name, constructArgumentsString(clazz, joinPoint.getArgs()));
  }
}

The ‘Before’ advice simply logs (at the appropriate log level) the method name and the toString value of all arguments (if any). ‘Before’ advice executes when a join point is reached.

@AfterThrowing(value = "@annotation(net.thoughtforge.aspect.Loggable)", throwing = "throwable", argNames = "joinPoint, throwable")
public void afterThrowing(JoinPoint joinPoint, Throwable throwable) {

  Class clazz = joinPoint.getTarget().getClass();
  String name = joinPoint.getSignature().getName();
  logger.log(LogLevel.ERROR, clazz, throwable, AFTER_THROWING, name, throwable.getMessage(), constructArgumentsString(clazz, joinPoint.getArgs()));
}

The ‘AfterThrowing’ advice logs the method name, exception message and the toString value of all arguments (if any). ‘AfterThrowing’ advice executes after a method exits by throwing an exception.

@AfterReturning(value = "@annotation(trace)", returning = "returnValue", argNames = "joinPoint, trace, returnValue")
public void afterReturning(JoinPoint joinPoint, Loggable loggable, Object returnValue) {

  Class clazz = joinPoint.getTarget().getClass();
  String name = joinPoint.getSignature().getName();

  if (joinPoint.getSignature() instanceof MethodSignature) {
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    Class returnType = signature.getReturnType();
    if (returnType.getName().compareTo("void") == 0) {
      logger.log(loggable.value(), clazz, null, AFTER_RETURNING_VOID, name, constructArgumentsString(clazz, returnValue));

      return;
    }
  }

  logger.log(loggable.value(), clazz, null, AFTER_RETURNING, name, constructArgumentsString(clazz, returnValue));
}

The ‘AfterReturning’ advice logs the method name and the toString value of the returned value (if any). ‘AfterReturning’ advice executes after a method exits normally.

You will notice that I log the toString value to identify objects. I routinely use the Apache Commons ToStringBuilder to create the toString value. I find this particularly useful when working with persistent entities as it allows me to clearly identify the entity.

Another possible implementation that avoids using the toString method is to use the Apache Commons ReflectionToStringBuilder within the logging aspect to create a string representation of the object being logged.

If you are writing your own toString implementations (the Commons implementation is perfectly adequate) and are implementing an object with complex properties be aware of recursive invocations that may result in a stack overflow exception.

Specifying the Pointcut

A pointcut expression is an expression that specifies where in the code the advice will be applied. With AspectJ, you can create a pointcut by specifying package, class and method attributes among other things. I find the easiest way to specify a pointcut for the logging aspect is by matching methods that have a specific annotation.

package net.thoughtforge.aspect;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import net.thoughtforge.logger.LogLevel;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Loggable {

  LogLevel value();
}

As you can see, the Loggable annotation has one property that specifies the log level at which the log statement should be output. Using the annotation means that developers never need to alter the pointcut expression to add or remove methods to the pointcut. A developer only has to add the annotation to a method to have the logging aspect applied.

package net.thoughtforge.bean;

import java.util.Date;

import net.thoughtforge.aspect.Loggable;
import net.thoughtforge.logger.LogLevel;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.springframework.stereotype.Component;

@Component(value = "simpleBean")
public class SimpleBean {

  private Date dateProperty;

  private Integer integerProperty;

  private String stringProperty;

  @Loggable(value = LogLevel.TRACE)
  public Date getDateProperty() {
    return dateProperty;
  }

  @Loggable(value = LogLevel.TRACE)
  public void setDateProperty(final Date dateProperty) {
    this.dateProperty = dateProperty;
  }

  @Loggable(value = LogLevel.TRACE)
  public Integer getIntegerProperty() {
    return integerProperty;
  }

  @Loggable(value = LogLevel.TRACE)
  public void setIntegerProperty(final Integer integerProperty) {
    this.integerProperty = integerProperty;
  }

  @Loggable(value = LogLevel.TRACE)
  public String getStringProperty() {
    return stringProperty;
  }

  @Loggable(value = LogLevel.TRACE)
  public void setStringProperty(final String stringProperty) {
    this.stringProperty = stringProperty;
  }

  @Override
  public String toString() {
    return new ToStringBuilder(this).append("dateProperty", dateProperty)
        .append("integerProperty", integerProperty).append("stringProperty", stringProperty).toString();
  }
}

The SimpleBean and SimpleBeanSubclass are for demonstration purposes. You can see that each method is annotated with the @Loggable annotation and the log level is set to TRACE. You can obviously use different log levels for different methods as required.

package net.thoughtforge.bean;

import java.math.BigDecimal;

import net.thoughtforge.aspect.Loggable;
import net.thoughtforge.logger.LogLevel;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.springframework.stereotype.Component;

@Component(value = "simpleBeanSubclass")
public class SimpleBeanSubclass extends SimpleBean {

  private BigDecimal decimalProperty;

  @Loggable(value = LogLevel.TRACE)
  public BigDecimal getDecimalProperty() {
    return decimalProperty;
  }

  @Loggable(value = LogLevel.TRACE)
  public void setDecimalProperty(final BigDecimal decimalProperty) {
    this.decimalProperty = decimalProperty;
  }

  @Override
  public String toString() {
    return new ToStringBuilder(this).append("decimalProperty", decimalProperty).appendSuper(super.toString()).toString();
  }
}

Also note the use of the ToStringBuilder to create the toString value. You may choose to use the ReflectionToStringBuilder or some other mechanism.

Testing the Logging Aspect

One way to test the logging aspect is to simply have a test invoke instrumented methods and observe the log statements produced. This is a useful exercise but requires manual intervention to determine if the test was successful.

In order to create an automated test for the logging aspect, I needed to create a mock logger that simulates the actions of a logger and allows me to interrogate the log statements produced.

package net.thoughtforge.mock.logger;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.thoughtforge.logger.LogLevel;
import net.thoughtforge.logger.Logger;

import org.springframework.stereotype.Component;

@Component
public class MockLogger implements Logger {

  private Map<Class<?>, LogLevel> logLevelMap = new HashMap<Class<?>, LogLevel>();

  private Map<Class<?>, List<LogMessage>> messages = new HashMap<Class<?>, List<LogMessage>>();

  public boolean isLogLevel(LogLevel logLevel,  Class clazz) {
    boolean result = false;

    switch (logLevel) {
      case DEBUG:
        result = isLogLevelEnabled(clazz, LogLevel.DEBUG);

      case ERROR:
        result = isLogLevelEnabled(clazz, LogLevel.ERROR);

      case FATAL:
        result = isLogLevelEnabled(clazz, LogLevel.FATAL);

      case INFO:
        result = isLogLevelEnabled(clazz, LogLevel.INFO);

      case TRACE:
        result = isLogLevelEnabled(clazz, LogLevel.TRACE);

      case WARN:
        result = isLogLevelEnabled(clazz, LogLevel.WARN);

      default:
        result = false;
    }

    return result;
  }

  public void log(LogLevel logLevel,  Class clazz, Throwable throwable,  String pattern, Object... arguments) {

    switch (logLevel) {
      case DEBUG:
        debug(clazz, throwable, pattern, arguments);
        break;

      case ERROR:
        error(clazz, throwable, pattern, arguments);
        break;

      case FATAL:
        fatal(clazz, throwable, pattern, arguments);
        break;

      case INFO:
        info(clazz, throwable, pattern, arguments);
        break;

      case TRACE:
        trace(clazz, throwable, pattern, arguments);
        break;

      case WARN:
        warn(clazz, throwable, pattern, arguments);
        break;
    }
  }

  private void debug(Class clazz,  Throwable throwable, String pattern,  Object... arguments) {

    if (throwable != null) {
      getMessages(clazz).add(new LogMessage(LogLevel.DEBUG, format(pattern, arguments), throwable));
    } else {
      getMessages(clazz).add(new LogMessage(LogLevel.DEBUG, format(pattern, arguments)));
    }
  }

  private void error(Class clazz,  Throwable throwable, String pattern,  Object... arguments) {

    if (throwable != null) {
      getMessages(clazz).add(new LogMessage(LogLevel.ERROR, format(pattern, arguments), throwable));
    } else {
      getMessages(clazz).add(new LogMessage(LogLevel.ERROR, format(pattern, arguments)));
    }
  }

  private void fatal(Class clazz,  Throwable throwable, String pattern,  Object... arguments) {

    if (throwable != null) {
      getMessages(clazz).add(new LogMessage(LogLevel.FATAL, format(pattern, arguments), throwable));
    } else {
      getMessages(clazz).add(new LogMessage(LogLevel.FATAL, format(pattern, arguments)));
    }
  }

  private void info(Class clazz,  Throwable throwable, String pattern,  Object... arguments) {

    if (throwable != null) {
      getMessages(clazz).add(new LogMessage(LogLevel.INFO, format(pattern, arguments), throwable));
    } else {
      getMessages(clazz).add(new LogMessage(LogLevel.INFO, format(pattern, arguments)));
    }
  }

  private void trace(Class clazz,  Throwable throwable, String pattern,  Object... arguments) {

    if (throwable != null) {
      getMessages(clazz).add(new LogMessage(LogLevel.TRACE, format(pattern, arguments), throwable));
    } else {
      getMessages(clazz).add(new LogMessage(LogLevel.TRACE, format(pattern, arguments)));
    }
  }

  private void warn( Class clazz,  Throwable throwable, String pattern,  Object... arguments) {

    if (throwable != null) {
      getMessages(clazz).add(new LogMessage(LogLevel.WARN, format(pattern, arguments), throwable));
    } else {
      getMessages(clazz).add(new LogMessage(LogLevel.WARN, format(pattern, arguments)));
    }
  }

  private String format( String pattern,  Object... arguments) {

    return MessageFormat.format(pattern, arguments);
  }

  public void resetLoggers() {
    messages = new HashMap, List&amp;gt;();
  }

  public List getMessages(Class clazz) {
    if (messages.get(clazz) == null) {
      messages.put(clazz, new ArrayList());
    }

    return messages.get(clazz);
  }

  private boolean isLogLevelEnabled(Class clazz, LogLevel logLevel) {
    return logLevelMap.get(clazz) != null && logLevelMap.get(clazz).equals(logLevel);
  }

  public void setLogLevel(Class clazz, LogLevel logLevel) {
    logLevelMap.put(clazz, logLevel);
  }

  public class LogMessage {
    private LogLevel logLevel;

    private String message;

    private Throwable throwable;

    public LogMessage(LogLevel logLevel, String message, Throwable throwable) {
      this(logLevel, message);
      this.throwable = throwable;
    }

    public LogMessage(LogLevel logLevel, String message) {
      this.logLevel = logLevel;
      this.message = message;
    }

    public LogLevel getLogLevel() {
      return logLevel;
    }

    public String getMessage() {
      return message;
    }

    public Throwable getThrowable() {
      return throwable;
    }
  }
}

The logging aspect test utilises the MockLogger and makes assertions about the log statements produced.

package net.thoughtforge.aspect;

import java.math.BigDecimal;

import junit.framework.Assert;
import net.thoughtforge.bean.SimpleBean;
import net.thoughtforge.bean.SimpleBeanSubclass;
import net.thoughtforge.logger.LogLevel;
import net.thoughtforge.mock.logger.MockLogger;
import net.thoughtforge.mock.logger.MockLogger.LogMessage;

import org.apache.commons.lang.time.DateUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
  "/applicationContext/applicationContext-aspect.xml",
  "/applicationContext/applicationContext-logger.xml",
  "/applicationContext/applicationContext.xml" })
public class LoggingAspectTest {

  @Autowired
  private MockLogger logger;

  @Autowired
  @Qualifier(value = &quot;simpleBean&quot;)
  public SimpleBean simpleBean;

  @Autowired
  public SimpleBeanSubclass simpleBeanSubclass;

  @Before
  public void before() {
    logger.setLogLevel(SimpleBean.class, LogLevel.TRACE);
    logger.setLogLevel(SimpleBeanSubclass.class, LogLevel.TRACE);
    logger.resetLoggers();
 }

  @Test
  public void testSimpleBean_SetDateProperty() throws Exception {
    simpleBean.setDateProperty(DateUtils.parseDate("01/01/2010", new String[] {"dd/MM/yyyy"}));

    Assert.assertEquals(2, logger.getMessages(SimpleBean.class).size());

    assertEquals(logger.getMessages(SimpleBean.class).get(0), LogLevel.TRACE,
        "[ entering < setDateProperty > with params Fri Jan 01 00:00:00 GMT 2010 ]");

    assertEquals(logger.getMessages(SimpleBean.class).get(1), LogLevel.TRACE,
        "[ leaving < setDateProperty > ]");
  }

  @Test
  public void testSimpleBean_SetIntegerProperty() {
    simpleBean.setIntegerProperty(100);

    Assert.assertEquals(2, logger.getMessages(SimpleBean.class).size());

    assertEquals(logger.getMessages(SimpleBean.class).get(0), LogLevel.TRACE,
        "[ entering < setIntegerProperty > with params 100 ]");

    assertEquals(logger.getMessages(SimpleBean.class).get(1), LogLevel.TRACE,
        "[ leaving < setIntegerProperty > ]");
  }

  @Test
  public void testSimpleBean_SetStringProperty() {
    simpleBean.setStringProperty("stringProperty");

    Assert.assertEquals(2, logger.getMessages(SimpleBean.class).size());

    assertEquals(logger.getMessages(SimpleBean.class).get(0), LogLevel.TRACE,
        "[ entering < setStringProperty > with params stringProperty ]");

    assertEquals(logger.getMessages(SimpleBean.class).get(1),LogLevel.TRACE,
        "[ leaving < setStringProperty > ]");
  }

  @Test
  public void testSimpleBean_GetDateProperty() {
    simpleBean.getDateProperty();

    Assert.assertEquals(2, logger.getMessages(SimpleBean.class).size());

    assertEquals(logger.getMessages(SimpleBean.class).get(0), LogLevel.TRACE,
        "[ entering < getDateProperty > ]");

    assertEquals(logger.getMessages(SimpleBean.class).get(1), LogLevel.TRACE,
        "[ leaving < getDateProperty > returning Fri Jan 01 00:00:00 GMT 2010 ]");
  }

  @Test
  public void testSimpleBean_GetIntegerProperty() {
    simpleBean.getIntegerProperty();

    Assert.assertEquals(2, logger.getMessages(SimpleBean.class).size());

    assertEquals(logger.getMessages(SimpleBean.class).get(0), LogLevel.TRACE,
        "[ entering < getIntegerProperty > ]");

    assertEquals(logger.getMessages(SimpleBean.class).get(1), LogLevel.TRACE,
        "[ leaving < getIntegerProperty > returning 100 ]");
  }

  @Test
  public void testSimpleBean_GetStringProperty() {
    simpleBean.getStringProperty();

    Assert.assertEquals(2, logger.getMessages(SimpleBean.class).size());

    assertEquals(logger.getMessages(SimpleBean.class).get(0), LogLevel.TRACE,
        "[ entering < getStringProperty > ]");

    assertEquals(logger.getMessages(SimpleBean.class).get(1), LogLevel.TRACE,
        "[ leaving < getStringProperty > returning stringProperty ]");
  }

  @Test
  public void testSimpleBeanSubclass_SetDateProperty() throws Exception {
    simpleBeanSubclass.setDateProperty(DateUtils.parseDate("01/01/2010", new String[] {"dd/MM/yyyy"}));

    Assert.assertEquals(2, logger.getMessages(SimpleBeanSubclass.class).size());

    assertEquals(logger.getMessages(SimpleBeanSubclass.class).get(0), LogLevel.TRACE,
        "[ entering < setDateProperty > with params Fri Jan 01 00:00:00 GMT 2010 ]");

    assertEquals(logger.getMessages(SimpleBeanSubclass.class).get(1), LogLevel.TRACE,
        "[ leaving < setDateProperty > ]");
  }

  @Test
  public void testSimpleBeanSubclass_SetDecimalProperty() {
    simpleBeanSubclass.setDecimalProperty(new BigDecimal("0.25"));

    Assert.assertEquals(2, logger.getMessages(SimpleBeanSubclass.class).size());

    assertEquals(logger.getMessages(SimpleBeanSubclass.class).get(0), LogLevel.TRACE,
        "[ entering < setDecimalProperty > with params 0.25 ]");

    assertEquals(logger.getMessages(SimpleBeanSubclass.class).get(1), LogLevel.TRACE,
        "[ leaving < setDecimalProperty > ]");
  }

  @Test
  public void testSimpleBeanSubclass_SetIntegerProperty() {
    simpleBeanSubclass.setIntegerProperty(100);

    Assert.assertEquals(2, logger.getMessages(SimpleBeanSubclass.class).size());

    assertEquals(logger.getMessages(SimpleBeanSubclass.class).get(0), LogLevel.TRACE,
        "[ entering < setIntegerProperty > with params 100 ]");

    assertEquals(logger.getMessages(SimpleBeanSubclass.class).get(1), LogLevel.TRACE,
        "[ leaving < setIntegerProperty > ]");
  }

  @Test
  public void testSimpleBeanSubclass_SetStringProperty() {
    simpleBeanSubclass.setStringProperty("stringProperty");

    Assert.assertEquals(2, logger.getMessages(SimpleBeanSubclass.class).size());

    assertEquals(logger.getMessages(SimpleBeanSubclass.class).get(0), LogLevel.TRACE,
        "[ entering < setStringProperty > with params stringProperty ]");

    assertEquals(logger.getMessages(SimpleBeanSubclass.class).get(1), LogLevel.TRACE,
        "[ leaving < setStringProperty > ]");
  }

  @Test
  public void testSimpleBeanSubclass_GetDateProperty() {
    simpleBeanSubclass.getDateProperty();

    Assert.assertEquals(2, logger.getMessages(SimpleBeanSubclass.class).size());

    assertEquals(logger.getMessages(SimpleBeanSubclass.class).get(0), LogLevel.TRACE,
        "[ entering < getDateProperty > ]");

    assertEquals(logger.getMessages(SimpleBeanSubclass.class).get(1), LogLevel.TRACE,
        "[ leaving < getDateProperty > returning Fri Jan 01 00:00:00 GMT 2010 ]");
  }

  @Test
  public void testSimpleBeanSubclass_GetDecimalProperty() {
    simpleBeanSubclass.getDecimalProperty();

    Assert.assertEquals(2, logger.getMessages(SimpleBeanSubclass.class).size());

    assertEquals(logger.getMessages(SimpleBeanSubclass.class).get(0), LogLevel.TRACE,
        "[ entering < getDecimalProperty > ]");

    assertEquals(logger.getMessages(SimpleBeanSubclass.class).get(1), LogLevel.TRACE,
        "[ leaving < getDecimalProperty > returning 0.25 ]");
  }

  @Test
  public void testSimpleBeanSubclass_GetIntegerProperty() {
    simpleBeanSubclass.getIntegerProperty();

    Assert.assertEquals(2, logger.getMessages(SimpleBeanSubclass.class).size());

    assertEquals(logger.getMessages(SimpleBeanSubclass.class).get(0), LogLevel.TRACE,
        "[ entering < getIntegerProperty > ]");

    assertEquals(logger.getMessages(SimpleBeanSubclass.class).get(1), LogLevel.TRACE,
        "[ leaving < getIntegerProperty > returning 100 ]");
  }

  @Test
  public void testSimpleBeanSubclass_GetStringProperty() {
    simpleBeanSubclass.getStringProperty();

    Assert.assertEquals(2, logger.getMessages(SimpleBeanSubclass.class).size());

    assertEquals(logger.getMessages(SimpleBeanSubclass.class).get(0), LogLevel.TRACE,
        "[ entering < getStringProperty > ]");

    assertEquals(logger.getMessages(SimpleBeanSubclass.class).get(1), LogLevel.TRACE,
        "[ leaving < getStringProperty > returning stringProperty ]");
  }

  private void assertEquals(LogMessage logMessage, LogLevel logLevel, String message) {
    Assert.assertEquals(logLevel, logMessage.getLogLevel());

    Assert.assertEquals(message, logMessage.getMessage());
  }
}

The test is not exhaustive but suffices to test the general operation of the logging aspect.

Final Word

Hopefully you found my example useful but I will finish by saying that there are many choices that I made in deciding on this approach.

A strong argument could be made for the use of XML configuration as opposed to the @Aspect approach which allows you greater control over the execution of the advice without modifying code. This would be particularly relevant in an environment where performance was a major focus. I personally have found that the control provided by logging frameworks to enable and disable particular loggers is sufficient in most environments.

Posted in Spring Framework
←
→
Logging In...
Cancel Reply
  • 19 Replies
  • 19 Comments
  • 0 Tweets
  • 0 Facebook
  • 0 Pingbacks
Last reply was February 9, 2012
  1. Mich
    View July 12, 2010

    Hi,

    Nice post very informative.
    I’ve tried the source code but instead of using spring 2.5. I’ve used spring 3.0.3 release version. However I’m having java.lang.NoClassDefFoundError
    at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.(AbstractAutoProxyCreator.java:118)
    at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.(AbstractAdvisorAutoProxyCreator.java:47)
    at org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator.(AspectJAwareAdvisorAutoProxyCreator.java:46)
    at org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.(AnnotationAwareAspectJAutoProxyCreator.java:48)

    Has anybody also encountered this? Is this a spring 3.0.3 issue?

    thanks,
    Mich

  2. Fred
    View August 5, 2010

    Hey,

    You forgot to add in the applicationContext.xml or maybe …

    But I have a problem with a similar annotation I’ve been asked to do. It works well, except when an annoted method calls another annoted in the same object. Only the first annotation reached will be logged :(

  3. Fred
    View August 5, 2010

    Sorry some symbols and what was inside has been removed from the comment.

    So I was saying you forgot to add the tag ” aop:aspectj-autoproxy/ ” or the tag ” bean class=”org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator” “.

    These tags are not necessary when configuring the aspect directly in XML.

  4. John Turner
    View August 5, 2010

    You are probably using proxy based AOP (i.e. aspects are applied via a proxy) and so when you invoke the second method you are invoking it directly, not through the proxy. This is documented in the Spring reference documentation.

    The alternatives to Springs proxy based AOP are to use run time or compile time byte code injection.

    Hope this helps.

  5. Mukul
    View May 2, 2011

    NICE Example,

    Give me a great idea to understand the AOP concepts.

    Thanks John :-)

  6. Santhosh
    View July 6, 2011

    Nice example…Has been very usefull in my project….

    One suggestion the below code in aspect Class clazz = joinPoint.getTarget().getClass(); will throw an error while applying the aspect to a static method.

    The below change works fine for all methods
    Class clazz = joinPoint.getSignature().getDeclaringType();

  7. John Turner
    View July 6, 2011

    Good point, and thanks for the feedback.

    It’s not something that has been a problem for me because a) I try to avoid the use of static methods where possible and b) I tend to use Springs AOP based proxy as opposed to AspectJ complie or load time instrumentation.

    Using getDeclaringType does return the super class in the tests provided with this example so it is not a like for like replacement.

  8. Way
    View July 28, 2011

    Hi John, can you elaborate what does the Before advice do?

    @Before(value = “@annotation(trace)”, argNames = “joinPoint, trace”)

    When I tried to type the same line exactly as you noted, my IDE complains about “trace”, and when I replaced it with “loggable”, it seems to work. So I am not sure if I am doing the right thing.

    This works for me:
    @Before(value = “@annotation(loggable)”, argNames = “joinPoint, loggable”)

    Thanks.

  9. John Turner
    View July 28, 2011

    The Before advice executes before a method is invoked.

    Your correction is right, I will update the post to reflect this. I must have been between renaming the parameters when I posted the code initially.

    Thanks

  10. Way
    View July 28, 2011

    John, thanks for the quick response. I have another question.

    Using your examples above, do you know how we can aspect log classes in a particular package at the class level, instead of method level. That way all the methods in the class can be traced instead of annotating each method at the finer level. I was thinking along the lines of using expression value in the advice but not sure if it is the correct way to do it, and how to code it using annotation style.

  11. John Turner
    View July 28, 2011

    That’s right. You can change the ‘value’ expression to select methods within a particular class. For example,

    @Before(value=”execution(* net.thoughtforge.bean.SimpleBean.*(..))”, argNames = “joinPoint”)

    Of course, we then lose the additional information that the annotation was providing i.e. the log level to apply.

  12. Sreekanth
    View September 13, 2011

    Please put the code on how to redirect this to log file.

    And need a sample class to invoke this, instaed of UnitTest class.

  13. John Turner
    View September 15, 2011

    In the applicationContext-logger.xml you should change the text

    bean id=”logger” class=”net.thoughtforge.mock.logger.MockLogger”

    to

    bean id=”logger” class=”net.thoughtforge.logger.CommonsLogger”

  14. Brian Repko
    View January 30, 2012

    John – I have a single project with a logging and performance aspect (@AspectJ style like this using spring-aop) and it works great. Now I’m changing from Hibernate to EclipseLink and looking to use spring-aspects / aspectj for compile time weaving of the JPA classes. My @AspectJ aspects no longer compile – can one mix both styles in a single project?

  15. John Turner
    View February 4, 2012

    Hi Brian, I’m not aware of any difficulty using both styles in a single project although it is not something that I have done myself. However, I have only tried this with load time and proxy based weaving. Do you have more specifics?

  16. Justen Britain
    View February 4, 2012

    I would like to use and modify the code you have provided in a commercial application. Can you please let me know if that’s ok and what license if any you are providing this under so I can place the appropriate documentation and licensing information in my code.

  17. John Turner
    View February 5, 2012

    Feel free to use any of the code samples I have provided in whatever way you see fit.

  18. Justen Britain
    View February 9, 2012

    Thanks John, Will credit you and your site in the source.

  19. RadhaKrishna
    View February 9, 2012

    hi ,

    can you explain how to save the log to external log file, the above sample is working for me

    thanks in advance

Tag Cloud

Agile Banking Book Review Cloud Computing Continuous Integration Enterprise Integration Groovy & Grails Hibernate JavaServer Faces NoSQL Object Relational Mapping Ramblings Representational State Transfer Rich Faces Software Design & Architecture Spring Framework Spring Integration Spring Security Spring Web Flow Training & Certification

Recent Posts

  • Continuous Delivery: A Maturity Assessment Model
  • Martin Fowler and No DBA
  • Marissa Mayer ends Work From Home
  • Lies, Damned Lies, and Statistics
  • 8th Light – The Principles of Craftsmanship

Top Posts & Pages

  • Creating a Logging Aspect with Spring AOP and AspectJ
  • Starting out with Spring and Hibernate JPA
  • Agile Testing: A Practical Guide for Testers and Agile Teams
  • Arduino Starter Kit - Day 1
  • Projects

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 14 other subscribers

  1. I welcome any feedback, questions or comments

Blogroll

  • Harvard Business Review
  • High Scalability
  • Mountain Goat Software

Pure Line theme by Theme4Press  •  Powered by WordPress Thought Forge