英文:
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) && 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("{}.{}({}) - {}", 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("{}.{} = {}", 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 abefore()
/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 viaproceed()
. - 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 theproceed()
call intotry-catch-finally
and conveniently handle and log any exceptions and stack traces and/or wrap checked exceptions into AspectJ'sSoftException
or a simpleRuntimeException
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 thearound()
advice. You can also return something else instead (but it must have the correct return type) or completely skipproceed()
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论