繁体   English   中英

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

[英]How to use AOP with AspectJ for logging?

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

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 是否有意义? 您知道任何开源软件,它就是这样做的吗?

我创建了一个简单的方面来捕获公共方法的执行。 这段 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

@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。 查看此帖子以获取更多详细信息。

您可以使用不同的切入点来满足您的需求。 文档将为您提供帮助。

直截了当的解决方案

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

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

注解

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> 

尝试在运行配置中将-javaagent:<path o>aspectjweaver-1.8.7.jar到您的参数中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM