如何使用AOP和AspectJ进行日志记录?

时间:2012-01-12 17:02:00

标签: java logging aop aspectj

我想在我的所有公共方法中添加“跟踪”消息,如下所示:

public void foo(s:String, n:int) { // log is a log4j logger or any other library
  log.trace(String.format("Enter foo with s: %s, n: %d", s, n))
  ...
  log.trace("Exit foo") 
}

现在我想用AOP(和字节码检测)自动将所有log.trace添加到我的方法中。我正在考虑AspectJ。是否有意义?你知道任何开放源代码吗?它确实如此?

6 个答案:

答案 0 :(得分:26)

我创建了一个简单的方面来捕获公共方法的执行。这个AspectJ代码的核心是切入点定义:

pointcut publicMethodExecuted(): execution(public * *(..));

这里我们使用任意数量的参数在任何包和任何类上捕获具有任何返回类型的所有公共方法。

建议执行可以在下面的代码片段中显示:

after(): publicMethodExecuted() {
    System.out.printf("Enters on method: %s. \n", thisJoinPoint.getSignature());

    Object[] arguments = thisJoinPoint.getArgs();
    for (int i =0; i < arguments.length; i++){
        Object argument = arguments[i];
        if (argument != null){
            System.out.printf("With argument of type %s and value %s. \n", argument.getClass().toString(), argument);
        }
    }

    System.out.printf("Exits method: %s. \n", thisJoinPoint.getSignature());
}

此建议使用 thisJoinPoint 来获取方法签名和参数。就是这样。这是方面代码:

public aspect LogAspect {

pointcut publicMethodExecuted(): execution(public * *(..));

after(): publicMethodExecuted() {
    System.out.printf("Enters on method: %s. \n", thisJoinPoint.getSignature());

    Object[] arguments = thisJoinPoint.getArgs();
    for (int i =0; i < arguments.length; i++){
        Object argument = arguments[i];
        if (argument != null){
            System.out.printf("With argument of type %s and value %s. \n", argument.getClass().toString(), argument);
        }
    }
    System.out.printf("Exits method: %s. \n", thisJoinPoint.getSignature());
}

对于更复杂的示例,我建议使用本书AspectJ: In Action

答案 1 :(得分:24)

来自@Loggable

jcabi-aspects注释和AspectJ方面是一个现成的机制(我是开发人员):

@Loggable(Loggable.DEBUG)
public String load(URL url) {
  return url.openConnection().getContent();
}

根据问题的要求记录进入和退出:

@Loggable(Loggable.DEBUG, prepend=true)
public String load(URL url) {
  return url.openConnection().getContent();
}

所有日志都转到SLF4J。查看this post了解详情。

答案 2 :(得分:4)

您可以使用不同的切入点来满足您的要求。这个documentation会对您有所帮助。

forward solution

答案 3 :(得分:1)

您可以尝试使用此开源http://code.google.com/p/perfspy/。 PerfSpy是一个运行时日志,性能监视和代码检查工具。它使用ApsectJ在运行时编织应用程序代码,并记录每个方法的执行时间及其输入参数和值。它有一个UI应用程序,您可以在其中查看方法调用及其输入和返回值作为树。有了它,您可以发现性能瓶颈并理解复杂的代码流。

答案 4 :(得分:0)

这是我记录输入,退出和记录方法异常的简单实现

注释

package test;

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

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface Audit {

}

拦截

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.logging.Level;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;


@Aspect
public class ExceptionInterceptor {

    private static final java.util.logging.Logger LOGGER = java.util.logging.Logger.getLogger(ExceptionInterceptor.class.getName());

    @Around("execution(* * (..))"
            + " && @annotation(test.Audit)"
    )
    public Object intercept(final ProceedingJoinPoint point) throws Throwable {
        final Method method
                = MethodSignature.class.cast(point.getSignature()).getMethod();
        String mName = method.getName();
        String cName = method.getDeclaringClass().getSimpleName();
        LOGGER.log(Level.INFO, "Entering {0}:{1}", new Object[]{cName, mName});
        Object out = null;
        try {
            out = point.proceed();
        } catch (Throwable t) {
            logExceptions(t, point);
        }
        LOGGER.log(Level.INFO, "Exiting {0}:{1}", new Object[]{cName, mName});
        return out;
    }

    private void logExceptions(Throwable t, final ProceedingJoinPoint point) {
        final Method method
                = MethodSignature.class.cast(point.getSignature()).getMethod();
        String mName = method.getName();
        String cName = method.getDeclaringClass().getSimpleName();
        Object[] params = point.getArgs();
        StringBuilder sb = new StringBuilder();
        sb.append("Exception caught for [");
        sb.append(cName);
        sb.append(".");
        sb.append(mName);
        for (int i = 0; i < params.length; i++) {
            Object param = params[i];

            sb.append("\n");
            sb.append("  [Arg=").append(i);
            if (param != null) {
                String type = param.getClass().getSimpleName();

                sb.append(", ").append(type);

                // Handle Object Array (Policy Override)
                if (param instanceof Object[]) {
                    sb.append("=").append(Arrays.toString((Object[]) param));
                } else {
                    sb.append("=").append(param.toString());
                }
            } else {
                sb.append(", null");
            }
            sb.append("]");
            sb.append("\n");
        }
        LOGGER.log(Level.SEVERE, sb.toString(), t);

    }
}

如何使用

@Audit  
public void testMethod(Int a,int b, String c){
}

Maven依赖项 编译

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.8.7</version>
    </dependency> 

编织

        <plugin>
            <groupId>com.jcabi</groupId>
            <artifactId>jcabi-maven-plugin</artifactId>
            <executions>
                <execution>
                    <phase>compile</phase>
                    <goals>
                        <goal>ajc</goal>
                    </goals>
                </execution>
            </executions>
        </plugin> 

答案 5 :(得分:-1)

尝试在运行配置中将Vue.use(VueResource) Vue.http.interceptors.push(function(request) { request.headers.set('X-CSRF-TOKEN', $('meta[name="csrf-token"]').attr('content')); }); 添加到参数中。