【问题标题】:Micrometer @Timed in Spring Boot REST method and Prometheus ErrorSpring Boot REST 方法中的 Micrometer @Timed 和 Prometheus 错误
【发布时间】:2020-01-21 19:11:58
【问题描述】:

我正在尝试使用 Micrometer 的 @Timed 注释从我的 Spring Boot 应用程序向 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);
    }

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

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]

【问题讨论】:

    标签: spring-boot prometheus spring-micrometer


    【解决方案1】:

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

    将其标记为已回答,但想了解其工作原理的一些信息。

    【讨论】:

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

    有同样的问题,使用 spring boot 2.0.2.RELEASE。按照 Bill Pfeiffer 的回答并将其添加到我的 pom 中,解决了这个问题。

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

    【讨论】:

    • 由于这只是重申现有答案,因此不应将其作为新答案发布。也就是说,我非常感谢您提供配置示例。
    • @JeremyCaney 同意,应该刚刚回复了 Bill Pfeiffer 的帖子
    • 添加此配置会导致错误提示!比尔告诉这个依赖应该被删除而不是添加,我的行为和他一样!
    猜你喜欢
    • 2021-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-14
    • 2022-07-07
    • 1970-01-01
    • 1970-01-01
    • 2019-10-25
    相关资源
    最近更新 更多