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&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 = "simpleBean")
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.

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
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
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.
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.
NICE Example,
Give me a great idea to understand the AOP concepts.
Thanks John
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();
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.
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.
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
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.
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.
Please put the code on how to redirect this to log file.
And need a sample class to invoke this, instaed of UnitTest class.
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”
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?
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?
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.
Feel free to use any of the code samples I have provided in whatever way you see fit.
Thanks John, Will credit you and your site in the source.
hi ,
can you explain how to save the log to external log file, the above sample is working for me
thanks in advance