【问题标题】:How to use AOP with AspectJ for logging?如何使用 AOP 和 AspectJ 进行日志记录?
【发布时间】:2020-07-17 19:32:51
【问题描述】:

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

public void foo(s:String, n:int) { // log 是 log4j 记录器或任何其他库
  log.trace(String.format("Enter foo with s: %s, n: %d", s, n))
  ...
  log.trace("退出 foo")
}

现在我想使用 AOP(和字节码检测)自动将所有这些 log.trace 添加到我的方法中。我正在考虑AspectJ。是否有意义?你知道有什么开源软件可以做到这一点吗?

【问题讨论】:

  • 是的,这是有道理的。 AspectJ 是开源的,Javaassist 也是如此。

标签: java logging aop aspectj


【解决方案1】:

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

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

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

建议执行可以在下面的代码 sn-p 上可视化:

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这本书。

【讨论】:

  • 当心System.out,你真的应该使用SLF4J这样的日志框架外观
【解决方案2】:

@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

【讨论】:

【解决方案3】:

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

forward solution

【讨论】:

  • 这就像另一个文档。不是一个清晰的解决方案
【解决方案4】:

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

【讨论】:

    【解决方案5】:

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

    注释

    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> 
    

    【讨论】:

    • 我使用了您的确切代码和项目设置,但从未调用过拦截器。有什么帮助吗?
    • 为什么在使用库的同时还要自己编写代码作为建议?
    【解决方案6】:

    尝试将-javaagent:&lt;path o&gt;aspectjweaver-1.8.7.jar 添加到运行配置中的参数中。

    【讨论】:

      猜你喜欢
      • 2012-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-22
      • 1970-01-01
      • 1970-01-01
      • 2011-04-05
      • 1970-01-01
      相关资源
      最近更新 更多