13

I'm trying to apply Prometheus metrics using the micrometer @Timed annotations. I found out that they only work on controller endpoints and not "simple" public and private methods.

Given this example:

@RestController
public class TestController {

    @GetMapping("/test")
    @Timed("test-endpoint") //does create prometheus metrics
    public String test() {
        privateMethod();
        publicMethod();
        return "test";
    }

    @Timed("test-private") //does NOT create prometheus metrics
    private void privateMethod() {System.out.println("private stuff");}

    @Timed("test-public") //does NOT create prometheus metrics
    public void publicMethod() {System.out.println("public stuff");}
}

creates the following metrics:

...
# HELP test_endpoint_seconds  
# TYPE test_endpoint_seconds summary
test_endpoint_seconds_count{class="com.example.micrometerannotationexample.TestController",exception="none",method="test",} 1.0
test_endpoint_seconds_sum{class="com.example.micrometerannotationexample.TestController",exception="none",method="test",} 0.0076286
# HELP test_endpoint_seconds_max  
# TYPE test_endpoint_seconds_max gauge
test_endpoint_seconds_max{class="com.example.micrometerannotationexample.TestController",exception="none",method="test",} 0.0076286
...

No metrics found for @Timed("test-private") and @Timed("test-public"), why is that?


Note: I've read on this github thread, that Spring Boot does not recognize @Timed annotations on arbitrary methods and that you need to manually configure a TimedAspect Bean in order for it to work. I've tried that but still it yields no results.

@Configuration
@EnableAspectJAutoProxy
public class MetricsConfig {
    @Bean
    public TimedAspect timedAspect(MeterRegistry registry) {
        return new TimedAspect(registry);
    }
}

To try this locally see necessary gist here

Edito
  • 3,030
  • 13
  • 35
  • 67

2 Answers2

18

@Timed works only on public methods called by another class.

Spring Boot annotations like @Timed / @Transactional need the so-called proxying which happens only between invocations of public methods.

A good explanation is this one https://stackoverflow.com/a/3429757/2468241

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
XII
  • 420
  • 4
  • 16
  • 1
    `The problem here is, that Spring's AOP proxies don't extend but rather wrap your service instance to intercept calls. This has the effect, that any call to "this" from within your service instance is directly invoked on that instance and cannot be intercepted by the wrapping proxy (the proxy is not even aware of any such call).` I'm not entirely up to speed with AOP but this answer from the same link in your answer explained it well for me. – Edito Mar 24 '22 at 13:19
1

I think a better example of how this works is demonstrated in this tutorial. This gives a great example of using the @Timed on controller calls and the results. Be aware that if your actuator endpoints are behind authentication you will need to add a bearer token to your prometheus config (see here).

Rhineb
  • 305
  • 3
  • 12