spring-micrometertimed

Extend annotation io.micrometer.core.annotation.Timed


I'm using @Timed in Spring Boot app for collecting metrics from methods and it works:

@Timed(percentiles = {0.95, 0.99}, histogram = true, extraTags = {"slice", "client"})

Since I'm using this annotation in the different places I decided to create custom annotation TimedExtended:

import io.micrometer.core.annotation.Timed;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Timed(percentiles = {0.95, 0.99}, histogram = true, extraTags = {"slice", "client"})
public @interface TimedExtended {
}

But this new one doesn't work, metrics are not being sent to Influx. Do you have idea what should be done for fixing it?


Solution

  • If you’re using a custom annotation that wraps @Timed (a meta-annotation), such as @TimedExtended, the pointcut from io.micrometer.core.aop.TimedAspect "execution (@io.micrometer.core.annotation.Timed * .(..))" won't match it directly. This is because the execution expression looks only for methods explicitly annotated with @Timed, and it won’t recognise methods annotated with @TimedExtended.

    For solving this problem I created my own Aspect, took as a base io.micrometer.core.aop.TimedAspect:

    @Aspect
    public class TimedExtendedAspect {
    ...
      @Around("execution (@com.test.metrics.TimedExtended * *.*(..))")
      @Nullable
      public Object timedMethod(ProceedingJoinPoint pjp) throws Throwable {
        if (shouldSkip.test(pjp)) {
          return pjp.proceed();
        }
    
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        TimedExtended timed = method.getAnnotation(TimedExtended.class);
        if (timed == null) {
          method = pjp.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes());
          timed = method.getAnnotation(TimedExtended.class);
        }
    
        return perform(pjp, timed, method);
      }
    ...
    }
    

    Also I modified my annotation in the next way:

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TimedExtended {
    
      String value() default "";
    
      boolean longTask() default false;
    
      String description() default "";
    
      String[] extraTags() default {"slice", "client"};
    
      boolean histogram() default true;
    
      double[] percentiles() default {0.95, 0.99};
    }
    

    Last step - create bean for new Aspect:

    @Bean
        TimedExtendedAspect timedExtendedAspect(InfluxMeterRegistry influxMeterRegistry) {
            return new TimedExtendedAspect(influxMeterRegistry);
        }