本文介绍了Micrometer TimedAspect 不会拦截对用@Timed 注释的方法的调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 Micrometer 在我的 Java 应用程序中记录执行时间.这与我的其他 有关关于使用的 @Timed 注释的问题.

I am trying to use Micrometer to record execution time in my Java application. This is related to my other question about used @Timed annotation.

我有一个 CountedObject 类,它有以下 2 个方法:

I have a class CountedObject that has the following 2 methods:

@Measured
@Timed(value = "timer1")
public void measuredFunction() {
    try {
        int sleepTime = new Random().nextInt(3) + 1;
        Thread.sleep(sleepTime * 1000L);
    } catch (InterruptedException e) {}
}

@Timed(value = "timer2")
public void timedFunction() {
    try {
        int sleepTime = new Random().nextInt(3) + 1;
        Thread.sleep(sleepTime * 1000L);
    } catch (InterruptedException e) {}
}

我定义了一个自定义注解 @Measured

I have defined a custom annotation @Measured

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Measured {
    
}

还有一个 MeasuredAspect 来拦截对用我的 @Measured 注释注释的方法的调用:

And a MeasuredAspect to intercept calls to methods annotated with my @Measured annotation:

@Aspect
public class MeasuredAspect {
    @Around("execution(* *(..)) && @annotation(Measured)")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        return AppMetrics.getInstance().handle(pjp);

    }
}

在我的 AppMetrics 类中,我初始化了一个千分尺的实例 TimedAspect 并在 handle(ProceedingJoinPoint pjp) 方法中传递 ProceedingJoinPoint pjp 到 TimedAspect 实例.

In my AppMetrics class I initialize an instance of micrometer's TimedAspect and in the handle(ProceedingJoinPoint pjp) method pass the ProceedingJoinPoint pjp to the TimedAspect instance.

public class AppMetrics {
    private static final AppMetrics instance = new AppMetrics();
    
    private MeterRegistry registry;
    private TimedAspect timedAspect;
    
    public static AppMetrics getInstance() {
        return instance;
    }
    
    private AppMetrics() {
        this.registry = new SimpleMeterRegistry();
        this.timedAspect = new TimedAspect(registry);
    }
    
    public Object handle(ProceedingJoinPoint pjp) throws Throwable {
        return timedAspect.timedMethod(pjp);
    }
}

在我的应用程序主程序中,我创建了一个 CountedObject 对象并调用了 measuredFunction()timedFunction() 然后我检查了我的 registry.getMeters(); 只找到measuredFunction() 使用的timer1 [由@Measured 和@Timed 注释],而timer2 应该由 timedFunction() 使用 [仅由 @Timed] 不存在.

In my application main, I create an object of CountedObject and invoke measuredFunction() and timedFunction() then I check my registry.getMeters(); only timer1 used by the measuredFunction() [which is annotated by both @Measured and @Timed] is found, while the timer2 that should be used by timedFunction() [annotated only by @Timed] doesn't exist.

我将 eclipse 与 AspectJ 开发工具插件一起使用,我的项目是一个带有 AspectJ 的 Gradle 项目能力.我正在使用 id "io.freefair.aspectj";我的 Gradle 插件中的版本5.1.1" 插件.这是一个基本的 Java 应用程序,而不是 Spring 应用程序.

I am using eclipse with AspectJ Development Tools Plugin and my project is a Gradle project with AspectJ capability. I am using id "io.freefair.aspectj" version "5.1.1" plugin in my Gradle plugins. This is a basic java application not a Spring app.

需要做哪些配置或者需要修改哪些代码才能使测微器TimedAspect 可以直接拦截我的方法调用【即timedFunction() 应该是定时和timer2 应该在注册表中找到] 而不需要我的自定义注释?

What configurations needs to be done or what code changes are required so that micrometer TimedAspect can intercept my method calls directly [i.e timedFunction() should be timed and timer2 should be found in the registry] without the need of my custom annotation?

推荐答案

我为您创建了一个示例项目:

I created an example project for you:

https://github.com/kriegaex/SO_AJ_MicrometerTimed_67803726

引用自述文件(抱歉,StackOverflow 上不赞成仅包含链接的答案):

Quoting the read-me (sorry, but answers only containing links are frowned upon on StackOverflow):

https://github.com/micrometer-metrics/micrometer/issues/1149 和 StackOverflow 上,关于 Micrometer @Timed 注释的常见问题解答是,为什么它可以与 Spring AOP 一起使用,但在编译时编织 (CTW) 的上下文中使用 Micrometer 作为本机 AspectJ 的方面库时却不能使用,例如使用 AspectJ Maven 插件.当提供指向 TimedAspectaop.xml 时,它可以与加载时编织 (LTW) 一起工作,但在 CTW 中,方面永远不会开始.

In https://github.com/micrometer-metrics/micrometer/issues/1149 and on StackOverflow, an FAQ about Micrometer's @Timed annotation is,why it works with Spring AOP, but not when using Micrometer as an aspect library for native AspectJ in the context of compile-time weaving (CTW),e.g. with AspectJ Maven Plugin. It can be made to work with load-time weaving (LTW) when providing an aop.xml pointing to TimedAspect,but in a CTW the aspect never kicks in.

原因是aspect是用Javac编译的,而不是用AspectJ编译器(AJC)编译的,这是完成"程序所必需的.Java 类,即增强其字节码以成为完整的 AspectJ 方面.LTW 代理在类加载期间即时执行此操作,但在 CTW 上下文中您需要明确告诉 AJC 在 Micrometer 库上进行编译后编织(也称为二进制编织),生成新编织的类文件.这是通过将 Micrometer 放在 AJC 的 inpath 上来完成的,以确保其类文件被转换并写入目标目录.AspectJ Maven 中的 inpath 是通过 配置的.至少有两种方法可以做到这一点:

The reason is that the aspect has been compiled with Javac, not with the AspectJ compiler (AJC), which is necessary to "finish" the Java class,i.e. to enhance its byte code in order to be a full AspectJ aspect. The LTW agent does that on the fly during class-loading, but in a CTW contextyou need to explicitly tell AJC to do post-compile weaving (a.k.a. binary weaving) on the Micrometer library, producing newly woven class files.This is done by putting Micrometer on AJC's inpath in order to make sure that its class files are being transformed and written to the targetdirectory. The inpath in AspectJ Maven is configured via <weaveDependencies>. There are at least two ways to do this:

  • 您可以在单独的 Maven 模块中创建自己的库的编织版本,然后使用该模块而不是 Micrometer.在这种情况下,您需要在消费模块中排除原始的 Micrometer 库,以确保 unwoven类文件不再位于类路径中,并且被意外使用.

  • You can either create your own woven version of the library in a separate Maven module and then use that module instead of Micrometer.In that case, you need to exclude the original Micrometer library in the consuming module, in order to make sure that the unwovenclass files are not on the classpath anymore and accidentally used.

本示例项目中显示的方式是单模块方法,使用 Maven Shade 构建可执行的 uber JAR.千分尺类文件不像第一种方法那样是一个可重用的库,但它非常适合演示目的,因为我们可以只运行示例应用程序并检查其输出:

The way shown here in this example project is a single-module approach, building an executable uber JAR with Maven Shade. The Micrometer classfiles are not a re-usable library like in the first approach, but it is nice for demonstration purposes, because we can just run the sampleapplication and check its output:

$ mvn clean package

...
[INFO] --- aspectj-maven-plugin:1.12.6:compile (default) @ SO_AJ_MicrometerTimed_67803726 ---
[INFO] Showing AJC message detail for messages of types: [error, warning, fail]
[INFO] Join point 'method-execution(void de.scrum_master.app.Application.doSomething())' in Type 'de.scrum_master.app.Application' (Application.java:23) advised by around advice from 'io.micrometer.core.aop.TimedAspect' (micrometer-core-1.7.0.jar!TimedAspect.class(from TimedAspect.java))
...
[INFO] --- maven-shade-plugin:3.2.4:shade (default) @ SO_AJ_MicrometerTimed_67803726 ---
[INFO] Including org.hdrhistogram:HdrHistogram:jar:2.1.12 in the shaded jar.
[INFO] Including org.latencyutils:LatencyUtils:jar:2.0.3 in the shaded jar.
[INFO] Including org.aspectj:aspectjrt:jar:1.9.6 in the shaded jar.
[INFO] Excluding io.micrometer:micrometer-core:jar:1.7.0 from the shaded jar.
[INFO] Replacing original artifact with shaded artifact.
[INFO] Replacing C:\Users\me\java-src\SO_AJ_MicrometerTimed_67803726\target\SO_AJ_MicrometerTimed_67803726-1.0-SNAPSHOT.jar with C:\Users\me\java-src\SO_AJ_MicrometerTimed_67803726\target\SO_AJ_MicrometerTimed_67803726-1.0-SNAPSHOT-shaded.jar
[INFO] Dependency-reduced POM written at: C:\Users\me\java-src\SO_AJ_MicrometerTimed_67803726\target\dependency-reduced-pom.xml
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

$ java -jar target/SO_AJ_MicrometerTimed_67803726-1.0-SNAPSHOT.jar

Juni 05, 2021 1:12:27 PM io.micrometer.core.instrument.push.PushMeterRegistry start
INFO: publishing metrics for LoggingMeterRegistry every 1m
Juni 05, 2021 1:13:00 PM io.micrometer.core.instrument.logging.LoggingMeterRegistry lambda$publish$5
INFO: method.timed{class=de.scrum_master.app.Application,exception=none,method=doSomething} throughput=0.166667/s mean=0.11842469s max=0.2146482s

请特别注意那些日志行(插入换行符以提高可读性):

Please specifically note those log lines (line breaks inserted for better readability):

Join point 'method-execution(void de.scrum_master.app.Application.doSomething())'
  in Type 'de.scrum_master.app.Application' (Application.java:23)
  advised by around advice from 'io.micrometer.core.aop.TimedAspect'
  (micrometer-core-1.7.0.jar!TimedAspect.class(from TimedAspect.java))

以上证明 @Timed 注释实际上导致 Micrometer 的 TimedAspect 被编织到我们的应用程序代码中.这里有方面为示例应用程序创建的度量:

The above is proof that the @Timed annotation actually causes Micrometer's TimedAspect to be woven into our application code. And here arethe measurements created by the aspect for the sample application:

method.timed
  {class=de.scrum_master.app.Application,exception=none,method=doSomething}
  throughput=0.166667/s mean=0.11842469s max=0.2146482s

这篇关于Micrometer TimedAspect 不会拦截对用@Timed 注释的方法的调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-23 09:36