I am trying to design an application that will show metrics about it's health and other custom metrics, like how long each function runs and how many times etc. I am on spring boot 2.7.1. I want to create metrics only by using annotations (@Counted, @Timed), but for some reason it is not working for me. It feels like I've tried almost every solution on the internet there is.

My yml file:

logging:
  level:
    root: "INFO"
server:
  port: 8083
spring:
  redis:
    host: "localhost"
    port: 6100
  task:
    scheduling:
      thread-name-prefix: "scheduling-"
      pool:
        size: 2

management:
  endpoint:
    health:
      show-details: always
      group:
        live:
          include: livenessState
        ready:
          include: readinessState, appReadinessIndicator
      probes:
        enabled: true
        livenessState: true
        readinessState: true
  endpoints:
    web:
      exposure:
        include: "*"

my main:

@SpringBootApplication
@ConfigurationPropertiesScan
@EnableScheduling
public class MyApp {

    public static void main(String[] args) {
       SpringApplication.run(MyApp.class, args);
    }

    @Bean
    RouterFunction<ServerResponse> routerFunction() {
       return route(GET("/health"), req ->
             ServerResponse.temporaryRedirect(URI.create("/actuator/health"))
                   .build());
    }

    @Bean
    RouterFunction<ServerResponse> routerFunction2() {
       return route(GET("/metrics"), req ->
             ServerResponse.temporaryRedirect(URI.create("/actuator/prometheus"))
                   .build());
    }
}

the class I am trying to get metrics from.

@Service
public class MyService {
    @Autowired
    private Template template;
    .........
    @Value 
    ....
    
    @Timed("MyTimer")
    @Counted("MyCounter")
    public void handleError() {
        ...
        code
        ...

    }

  

    public void handleMessage (param){
       if (message == null || message.getInfo() == null) {
           this.HandleError();
       }else if 
       ...


   }

   @PostConstruct
   public void setupSubscriber() {
    for (Map.Entry<String, NamespaceConfiguration> namespaceConfiguration : myConfig.getNamespaces().entrySet()) {
        if (!namespaceConfiguration.getKey().equals(MyConfig.NAMESPACE)) {
            this.template.listenToPattern(namespaceConfiguration.getValue().getRequest())
                    .doOnSubscribe(c -> logger.info("Successfully subscribed to {}", namespaceConfiguration.getValue().getRequest()))
                    .doOnNext(msg -> logger.info(msg.getChannel() + ": Received msg " + msg.getMessage()))
                    .onErrorContinue(this::handleError) 
                    .log("OUTER", Level.INFO)
                    .subscribe(this::handleMessage);
        }
    }
    myReadinessIndicator.markAsReady();
}

For it to work I've tried adding this. But it didn't work either.

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

Only solution that worked is this one. But with a lot of methods its repetitive. I've also tried moving method to separate class. Only with this solution I've found my metrics in

http://localhost:port/actuator/prometheus

and also in

http://localhost:port/actuator/metrics
@Service
@Component
public class MyService {
    @Autowired
    private Template template;
    .........
    @Value 
    ....

    public MyService (MeterRegistry registry){

        Counter timeElapsedHandleError = Counter.builder("time.handleError").
                description("Time in ms of run fnc handleError").
                register(registry);
        Counter countRunHandleError = Counter.builder("count.handleError").
                description("Counts how many times fnc handleError was run").
                register(registry);

        ...
    }

    public void handleError() {
        long startTime = System.nanoTime();
        metrics.countErrors.increment();

        ...
        code
        ...

        metrics.timeErrors.increment(System.nanoTime()-startTime);

    }



    public void handleMessage (param){
       if (message == null || message.getInfo() == null) {
           this.HandleError();
       }

   }

   @PostConstruct
   public void setupSubscriber() {
    for (Map.Entry<String, NamespaceConfiguration> namespaceConfiguration : myConfig.getNamespaces().entrySet()) {
        if (!namespaceConfiguration.getKey().equals(MyConfig.NAMESPACE)) {
            this.template.listenToPattern(namespaceConfiguration.getValue().getRequest())
                    .doOnSubscribe(c -> logger.info("Successfully subscribed to {}", namespaceConfiguration.getValue().getRequest()))
                    .doOnNext(msg -> logger.info(msg.getChannel() + ": Received msg " + msg.getMessage()))
                    .onErrorContinue(this::handleError) 
                    .log("OUTER", Level.INFO)
                    .subscribe(this::handleMessage);
        }
    }
    myReadinessIndicator.markAsReady();
}

I've tried moving method to another class like this. Am I doing it wrong?

@Service
@Component
public class MyService {
    @Autowired
    private Template template;
    .........
    @Value 
    ....
    
    @Autowired
    ServiceHandler serviceHandler;

    @Autowire 
    MessageHandler messageHandler;
    

   @PostConstruct
   public void setupSubscriber() {
    for (Map.Entry<String, NamespaceConfiguration> namespaceConfiguration : myConfig.getNamespaces().entrySet()) {
        if (!namespaceConfiguration.getKey().equals(MyConfig.NAMESPACE)) {
            this.template.listenToPattern(namespaceConfiguration.getValue().getRequest())
                    .doOnSubscribe(c -> logger.info("Successfully subscribed to {}", namespaceConfiguration.getValue().getRequest()))
                    .doOnNext(msg -> logger.info(msg.getChannel() + ": Received msg " + msg.getMessage()))
                    .onErrorContinue(serviceHandler::handleError) 
                    .log("OUTER", Level.INFO)
                    .subscribe(messageHandler::handleMessage);
        }
    }
    myReadinessIndicator.markAsReady();
}

I need to call multiple methods from handleMessage(). I've tried moving it to separate class.

@Component
public class MessageHandler{

    @Autowire
    ServiceHanler serviceHanler;

    public void handleMessage (param){
       if (message == null || message.getInfo() == null) {
           serviceHanler.HandleError();
       }else if (something){
           serviceHandler.handleXYMessage();
       }

   }
}

and the second class:

@Component
public class ServiceHandler {

    @Timed("MyTimer")
    @Counted("MyCounter")
    public void handleError() {
        ...
        code
        ...

    }


    @Timed("MyTimer2")
    @Counted("MyCounter2")
    public void handleXYMessage(){
       ...
       code
       ...

    }


}

So I resolved the issue. The problem was with call of the function with the annotation. I was expecting to see metrics right away after application start up. But the metrics show up after the function was run.

0

There are 0 answers