Spring Boot REST 方法中的 Micrometer @Timed 和 Prometheus 错误

Bil*_*fer 3 spring-boot prometheus spring-micrometer

我正在尝试使用 Micrometer 的 @Timed 注释从我的 Spring Boot 应用程序向 Prometheus 发送数据,并且看到了奇怪的行为。我在 prometheus 中看到了我的方法的数据,但我也看到它在某些情况下失败,但下面有例外。我包含了必要的库,并遵循了 Spring Boot 配置说明来添加 Micrometer @Timed 注释。在构建指标的键时,感觉像是 Spring Boot / Micrometer 错误。我的定时注释很简单:

    @GetMapping(value = {"/pageable"}, produces = MediaType.APPLICATION_JSON_VALUE)
    @Timed("employees.get.pageable")
    public Page<Employee> all(@SortDefault(sort = "id", direction = Sort.Direction.ASC) Pageable p) {
        return (Page<Employee>) employeeRepository.findAll(p);
    }
Run Code Online (Sandbox Code Playgroud)

任何想法我可能做错了什么?我很乐意回复额外的配置,但不想在这里粘贴我的整个项目。堆栈跟踪:

2020-01-21 18:53:40.559 ERROR 1 --- [nio-8080-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
java.lang.IllegalArgumentException: Prometheus requires that all meters with the same name have the same set of tag keys. There is already an existing meter named 'companies_get_single_seconds' containing tag keys [class, exception, method]. The meter you are attempting to register has keys [exception, method, outcome, status, uri].
    at io.micrometer.prometheus.PrometheusMeterRegistry.lambda$collectorByName$9(PrometheusMeterRegistry.java:382) ~[micrometer-registry-prometheus-1.3.2.jar!/:1.3.2]
    at java.base/java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1932) ~[na:na]
    at io.micrometer.prometheus.PrometheusMeterRegistry.collectorByName(PrometheusMeterRegistry.java:369) ~[micrometer-registry-prometheus-1.3.2.jar!/:1.3.2]
    at io.micrometer.prometheus.PrometheusMeterRegistry.newTimer(PrometheusMeterRegistry.java:175) ~[micrometer-registry-prometheus-1.3.2.jar!/:1.3.2]
    at io.micrometer.core.instrument.MeterRegistry.lambda$timer$2(MeterRegistry.java:270) ~[micrometer-core-1.1.7.jar!/:1.1.7]
    at io.micrometer.core.instrument.MeterRegistry.getOrCreateMeter(MeterRegistry.java:575) ~[micrometer-core-1.1.7.jar!/:1.1.7]
    at io.micrometer.core.instrument.MeterRegistry.registerMeterIfNecessary(MeterRegistry.java:528) ~[micrometer-core-1.1.7.jar!/:1.1.7]
    at io.micrometer.core.instrument.MeterRegistry.timer(MeterRegistry.java:268) ~[micrometer-core-1.1.7.jar!/:1.1.7]
    at io.micrometer.core.instrument.Timer$Builder.register(Timer.java:464) ~[micrometer-core-1.1.7.jar!/:1.1.7]
    at io.micrometer.core.instrument.composite.CompositeTimer.registerNewMeter(CompositeTimer.java:140) ~[micrometer-core-1.1.7.jar!/:1.1.7]
    at io.micrometer.core.instrument.composite.CompositeTimer.registerNewMeter(CompositeTimer.java:31) ~[micrometer-core-1.1.7.jar!/:1.1.7]
    at io.micrometer.core.instrument.composite.AbstractCompositeMeter.add(AbstractCompositeMeter.java:66) ~[micrometer-core-1.1.7.jar!/:1.1.7]
    at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[na:na]
    at java.base/java.util.Collections$SetFromMap.forEach(Collections.java:5581) ~[na:na]
    at io.micrometer.core.instrument.composite.CompositeMeterRegistry.lambda$null$0(CompositeMeterRegistry.java:65) ~[micrometer-core-1.1.7.jar!/:1.1.7]
    at io.micrometer.core.instrument.composite.CompositeMeterRegistry.lock(CompositeMeterRegistry.java:184) ~[micrometer-core-1.1.7.jar!/:1.1.7]
    at io.micrometer.core.instrument.composite.CompositeMeterRegistry.lambda$new$1(CompositeMeterRegistry.java:65) ~[micrometer-core-1.1.7.jar!/:1.1.7]
    at io.micrometer.core.instrument.MeterRegistry.getOrCreateMeter(MeterRegistry.java:585) ~[micrometer-core-1.1.7.jar!/:1.1.7]
    at io.micrometer.core.instrument.MeterRegistry.registerMeterIfNecessary(MeterRegistry.java:528) ~[micrometer-core-1.1.7.jar!/:1.1.7]
    at io.micrometer.core.instrument.MeterRegistry.timer(MeterRegistry.java:268) ~[micrometer-core-1.1.7.jar!/:1.1.7]
    at io.micrometer.core.instrument.Timer$Builder.register(Timer.java:464) ~[micrometer-core-1.1.7.jar!/:1.1.7]
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.stop(WebMvcMetricsFilter.java:180) ~[spring-boot-actuator-2.1.9.RELEASE.jar!/:2.1.9.RELEASE]
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.record(WebMvcMetricsFilter.java:174) ~[spring-boot-actuator-2.1.9.RELEASE.jar!/:2.1.9.RELEASE]
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:130) ~[spring-boot-actuator-2.1.9.RELEASE.jar!/:2.1.9.RELEASE]
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:104) ~[spring-boot-actuator-2.1.9.RELEASE.jar!/:2.1.9.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.1.10.RELEASE.jar!/:5.1.10.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.26.jar!/:9.0.26]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.26.jar!/:9.0.26]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.1.10.RELEASE.jar!/:5.1.10.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.1.10.RELEASE.jar!/:5.1.10.RELEASE]
Run Code Online (Sandbox Code Playgroud)

Bil*_*fer 5

大多数关于带有千分尺的 Spring Boot Actuator 计时器指标的帖子都添加了 Spring Boot aop 库并在配置中使用了 TimedAspect bean。我从配置中删除了 TimedAspect 并从我的依赖项中删除了 spring-boot-starter-aop,但保留了我的 @Timed 注释,问题就消失了。我假设(不确定)Spring 和 Micrometer 都使用不同的标签键注册相同的指标。通过此设置,我可以在 prometheus 中获取自定义指标,没有更多例外。

将此标记为已回答,但希望了解有关其工作原理的一些信息。

  • 默认情况下,Spring Boot Actuator 会向每个控制器端点添加一个指标。因此,在控制器方法上添加 @Timed("...") 时会出现此错误(您对双重注册的看法是正确的)。例如,如果您注释服务方法,则不应出现错误。我仍在寻找合适的会议来避免这种情况。 (3认同)
  • 将“@Timed”添加到控制器(springboot2)中的端点之一时,我遇到了同样的错误。这是因为我的代码中有一个 TimedAspect 类型的 bean。通常,如果您只将“@Timed”添加到 RestController,则不需要定义 TimedAspect bean,但就我而言,我希望能够在应用程序的其他部分使用计时器,而“@Timed”则不是当它在控制器之外时被考虑。创建 TimedAspect bean 解决了该问题,但在注释控制器方法时会导致此问题。考虑将“@Timed”移至服务层 (2认同)
  • 我采取了相反的方法并禁用了“WebMvcMetricsAutoConfiguration”。这样,所有“@Timed”注释都只能由 TimedAspect 获取。缺点是您会丢失自动配置提供的标签处理 (2认同)