Отображение произвольных @Timed метрик в prometheus

#prometheus #spring-boot-actuator #micrometer

#prometheus #пружинный загрузочный привод #микрометр

Вопрос:

Я пытаюсь добавить временные метрики в свое веб-приложение с весенней загрузкой. Прямо сейчас приложение использует micrometer, prometheus и spring-boot-actuator.

Я могу подключиться к своему приложению по http://localhost:8080/actuator/prometheus и посмотрите список показателей по умолчанию, таких как:

 # HELP jvm_gc_memory_allocated_bytes_total Incremented for an increase in the size of the young generation memory pool after one GC to before the next
# TYPE jvm_gc_memory_allocated_bytes_total counter
jvm_gc_memory_allocated_bytes_total{application="my app",} 0.0
# HELP jvm_classes_loaded_classes The number of classes that are currently loaded in the Java virtual machine
# TYPE jvm_classes_loaded_classes gauge
jvm_classes_loaded_classes{application="my app",} 7581.0
...
  

Потрясающе!

Но теперь я хотел бы привязать временные метрики к методам одного из моих контроллеров. Я надеялся, что это так же просто, как добавить @Timed аннотацию к классу:

 import java.util.concurrent.atomic.AtomicLong;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import io.micrometer.core.annotation.Timed;

@RestController
@Timed
public class GreetingController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

    @GetMapping("/greeting")
    public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
        doWork();
        return buildResponse(name);
    }

    private void doWork() {
        try {
            Thread.sleep((long) (1000 * Math.random()));
        } catch (InterruptedException e) {
        }
    }

    private Greeting buildResponse(String name) {
        return new Greeting(counter.incrementAndGet(), String.format(template, name));
    }
}
  

Однако, похоже, это не добавляет никаких метрик к http://localhost:8080/actuator/prometheus конечная точка.

Какие шаги мне нужно предпринять, чтобы конечная точка сообщала о временных метриках GreetingController класса и, в частности doWork , buildResponse о методах and ?

— обновить —

Мне удалось получить желаемые результаты, но потребовалось немного больше работы, чем я ожидал, и я надеюсь, что есть более простой способ. Я явно добавил Timer объекты к каждому методу, который я хотел измерить, вот так:

 import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;

@RestController
public class GreetingController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

    @Autowired
    MeterRegistry meterRegistry;

    @GetMapping("/greeting")
    public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
        doWork();
        return buildResponse(name);
    }

    private void doWork() {
        Timer timer = meterRegistry.timer(this.getClass().getSimpleName()   ".doWork");
        timer.record(() -> {
            try {
                Thread.sleep((long) (1000 * Math.random()));
            } catch (InterruptedException e) {
            }
        });
    }

    private Greeting buildResponse(String name) {
        AtomicReference<Greeting> response = new AtomicReference<>();

        Timer timer = meterRegistry.timer(this.getClass().getSimpleName()   ".buildResponse");
        timer.record(() -> {
            response.set(new Greeting(counter.incrementAndGet(), String.format(template, name)));
        });

        return response.get();
    }
}
  

Но, по крайней мере, это дает мне результаты, которые я искал:

 # HELP GreetingController_buildResponse_seconds  
# TYPE GreetingController_buildResponse_seconds summary
GreetingController_buildResponse_seconds_count{application="my app",} 10.0
GreetingController_buildResponse_seconds_sum{application="my app",} 0.001531409
# HELP GreetingController_buildResponse_seconds_max  
# TYPE GreetingController_buildResponse_seconds_max gauge
GreetingController_buildResponse_seconds_max{application="my app",} 2.52253E-4
# HELP GreetingController_doWork_seconds_max  
# TYPE GreetingController_doWork_seconds_max gauge
GreetingController_doWork_seconds_max{application="my app",} 0.941169892
# HELP GreetingController_doWork_seconds  
# TYPE GreetingController_doWork_seconds summary
GreetingController_doWork_seconds_count{application="my app",} 10.0
GreetingController_doWork_seconds_sum{application="my app",} 4.767700907
  

Есть ли более чистый способ?

Ответ №1:

Зарегистрируйте TimedAspect как компонент в классе конфигурации:

 @Bean
public TimedAspect timedAspect(MeterRegistry meterRegistry) {
    return new TimedAspect(meterRegistry);
}
  

Ответ №2:

в моем случае prometheusMeterRegistry были созданы слишком рано, и необходимо было применить следующий взлом

 @Configuration
@ConditionalOnProperty(value = "app.metrics.enabled", matchIfMissing = true, havingValue = "true")
static class AppMetricsConfig {

    @Bean
    InitializingBean forcePrometheusPostProcessor(BeanPostProcessor meterRegistryPostProcessor,
                                                  PrometheusMeterRegistry registry) {
      return () -> meterRegistryPostProcessor.postProcessAfterInitialization(registry, "");
    }

    /**
     * This is required so that we can use the @Timed annotation
     * on methods that we want to time.
     * See: https://micrometer.io/docs/concepts#_the_timed_annotation
     */
    @Bean
    public TimedAspect timedAspect(HikariDataSource dataSource, MeterRegistry registry) {
      return new TimedAspect(registry);
    }
}
  

подробнее https://github.com/micrometer-metrics/micrometer/issues/513

Ответ №3:

@Timed(«Приветствие») должен добавлять тег