使用Eclipse AspectJ注入日志记录器,记录代码执行的上下文/元数据。

huangapple go评论89阅读模式
英文:

Using Eclipse AspectJ to Inject a Logger which Logs context/meta data of the code execute?

问题

我正在尝试定义一个切面以注入日志记录器。

我希望创建类似于以下内容的东西:

```aspectj
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public aspect LogInjector {

    private pointcut executionJoinPoints(): !within(LogInjector) && execution (* *.*(..));

    before(): executionJoinPoints(){
        // 获取执行代码的类名
        clazz = ...
        final Logger logger = LogManager.getLogger(clazz);

        // 获取执行代码的方法名
        method = ...

        // 获取执行代码的参数名称、类型和值三元组,或者至少获取值,如果前者不可行
        params = ...

        // 获取执行代码的调用堆栈
        stack = ...

        logger.trace("{}.{}({}) - {}", clazz.name(), method.name(), params, stack);
    }

    after(): executionJoinPoints(){
        // 获取执行代码的类名
        clazz = ...
        final Logger logger = LogManager.getLogger(clazz);

        // 获取执行代码的方法名
        method = ...

        // 获取执行代码的返回值或异常
        result = ...

        logger.trace("{}.{} = {}", clazz.name(), method.name(), result);
    }
}

为此,我想要获取执行元数据/上下文数据:

  • 异常
  • 返回值

如何获取这些元数据/上下文数据?


<details>
<summary>英文:</summary>

I am trying to define an aspect to inject a logger.

I am looking to create something like:

```aspectj
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public aspect LogInjector {

    private pointcut executionJoinPoints(): !within(LogInjector) &amp;&amp; execution (* *.*(..));

    before(): executionJoinPoints(){
        // Get class name of the executed code
        clazz = ...
        final Logger logger = LogManager.getLogger(clazz);

        // Get method name of the executed code
        method = ...

        // Get params name, type and values triplet or values at least if the previous is not possible, of the executed code
        params = ...

        // Get call stack of the executed code
        stack = ...

        logger.trace(&quot;{}.{}({}) - {}&quot;, clazz.name(), method.name(), params, stack);
    }

    after(): executionJoinPoints(){
        // Get class name of the executed code
        clazz = ...
        final Logger logger = LogManager.getLogger(clazz);

        // Get method name of the executed code
        method = ...

        // Get return value or exception of the executed code
        result = ...

        logger.trace(&quot;{}.{} = {}&quot;, clazz.name(), method.name(), result);
    }
}

For this I want to retrieve execution metadata/context data:

  • exceptions
  • return values

How can get this metadata/context data?

答案1

得分: 1

为了保持您的方面(Aspect)高效,我建议采取以下措施:

  • 将您的切入点(Pointcut)限制在目标包和类上,这些是您真正希望进行调试的。不要记录/追踪整个世界。您还可以使用抽象基础方面(Aspect),其中包含一个抽象切入点,然后通过具体的子方面(Sub-Aspect)扩展该方面,并在具体切入点中进行具体设置。如果您使用加载时织入(Load Time Weaving),甚至可以通过 XML 配置提供后者。
  • 使用 around() 建议(Advice)而不是 before() / after() 对。然后,您只需要计算一些记录的值一次,并在通过 proceed() 进行的原始方法调用之前和之后都使用这些值。
  • 简单地记录 thisJoinPoint,而不是默认情况下拼凑其中包含的部分。这已经可以为您提供连接点的类型、方法签名(包括参数类型和返回值)。
  • 不要记录参数名称,这些信息并没有真正的价值。此外,参数名称可能会被重构,并且仅在您的代码使用调试信息进行编译时存在。保持简单,只记录参数值。
  • 在上面提到的 around() 建议中,您可以将 proceed() 调用放入 try-catch-finally 中,并方便地处理和记录任何异常和堆栈跟踪,或者将已检查异常包装成 AspectJ 的 SoftException 或简单的 RuntimeException 并重新抛出它们,具体取决于您的情况。
  • 方法调用结果只是 proceed() 的结果,您还需要从 around() 建议中返回它。您也可以返回其他内容(但必须具有正确的返回类型),或者如果出于任何原因希望跳过目标方法执行,可以完全跳过 proceed()

我刚刚说的所有内容都写在 AspectJ 手册或任何其他 AspectJ 教程中。在下次提出这样的一般性问题之前,您可能希望阅读其中一些内容。

英文:

In order to keep your aspect efficient, I recommend the following:

  • Limit your pointcut to the target packages and classes you really wish to debug. Don't log/trace the whole world. You could also use an abstract base aspect with an abstract pointcut and extend the aspect into a concrete sub-aspect with a concrete pointcut. The latter can even be provided via XML configuration if you use load time weaving.
  • Use an around() advice instead of a before() / after() pair. Then you only need to calculate some of the logged values once and use them both before and after the original method call done via proceed().
  • Simply log thisJoinPoint instead of piecing together bits contained therein by default. This will already give you the type of joinpoint, method signature including parameter types and return value.
  • Don't log parameter names, the information adds no real value. Furthermore, parameter names are subject to refactoring and are only present if your code is compiled with debug information. Keep it simple and only log the parameter values.
  • In the around() advice mentioned above you can enclose the proceed() call into try-catch-finally and conveniently handle and log any exceptions and stack traces and/or wrap checked exceptions into AspectJ's SoftException or a simple RuntimeException and re-throw them. Whatever is applicable to your situation.
  • Method call results are just the results of proceed(), which would you also be what you need to return from the around() advice. You can also return something else instead (but it must have the correct return type) or completely skip proceed() if for whatever reason you wish to skip target method execution.

All of what I just said is written in the AspectJ manual or in any other AspectJ tutorial. You might want to read some of those next time before asking a general question like this one.

huangapple
  • 本文由 发表于 2020年9月21日 16:30:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/63988698.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定