In simple Spring Boot 2.3 app that exposes metrics over Prometheus endpoint:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

we enable MBean Registry to get Tomcat metrics:

server.tomcat.mbeanregistry.enabled=true

then we customize naming convention:

@Bean
public MeterRegistryCustomizer<MeterRegistry> prometheusNamingConvention() {
    return registry -> registry
            .config()
            .namingConvention(NamingConvention.snakeCase);
}

When we enable NamingConvention.snakeCase TomcatMetrics fails when registering tomcat_global_request_max metric:

java.lang.IllegalArgumentException: Collector already registered that provides name: tomcat_global_request_max
        at io.prometheus.client.CollectorRegistry.register(CollectorRegistry.java:54) ~[simpleclient-0.8.1.jar:na]
        at io.prometheus.client.Collector.register(Collector.java:139) ~[simpleclient-0.8.1.jar:na]
        at io.micrometer.prometheus.PrometheusMeterRegistry.lambda$applyToCollector$16(PrometheusMeterRegistry.java:408) ~[micrometer-registry-prometheus-1.5.9.jar:1.5.9]
        at java.base/java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1916) ~[na:na]
        at io.micrometer.prometheus.PrometheusMeterRegistry.applyToCollector(PrometheusMeterRegistry.java:404) ~[micrometer-registry-prometheus-1.5.9.jar:1.5.9]
        at io.micrometer.prometheus.PrometheusMeterRegistry.newGauge(PrometheusMeterRegistry.java:206) ~[micrometer-registry-prometheus-1.5.9.jar:1.5.9]
        at io.micrometer.core.instrument.MeterRegistry.newTimeGauge(MeterRegistry.java:201) ~[micrometer-core-1.5.9.jar:1.5.9]
        at io.micrometer.core.instrument.MeterRegistry$More.lambda$timeGauge$3(MeterRegistry.java:977) ~[micrometer-core-1.5.9.jar:1.5.9]
        at io.micrometer.core.instrument.MeterRegistry.lambda$registerMeterIfNecessary$5(MeterRegistry.java:559) ~[micrometer-core-1.5.9.jar:1.5.9]
        at io.micrometer.core.instrument.MeterRegistry.getOrCreateMeter(MeterRegistry.java:612) ~[micrometer-core-1.5.9.jar:1.5.9]
        at io.micrometer.core.instrument.MeterRegistry.registerMeterIfNecessary(MeterRegistry.java:566) ~[micrometer-core-1.5.9.jar:1.5.9]
        at io.micrometer.core.instrument.MeterRegistry.registerMeterIfNecessary(MeterRegistry.java:559) ~[micrometer-core-1.5.9.jar:1.5.9]
        at io.micrometer.core.instrument.MeterRegistry.access$600(MeterRegistry.java:76) ~[micrometer-core-1.5.9.jar:1.5.9]
        at io.micrometer.core.instrument.MeterRegistry$More.timeGauge(MeterRegistry.java:977) ~[micrometer-core-1.5.9.jar:1.5.9]
        at io.micrometer.core.instrument.TimeGauge$Builder.register(TimeGauge.java:117) ~[micrometer-core-1.5.9.jar:1.5.9]
        at io.micrometer.core.instrument.binder.tomcat.TomcatMetrics.lambda$registerGlobalRequestMetrics$33(TomcatMetrics.java:224) ~[micrometer-core-1.5.9.jar:1.5.9]
        at io.micrometer.core.instrument.binder.tomcat.TomcatMetrics.lambda$registerMetricsEventually$34(TomcatMetrics.java:237) ~[micrometer-core-1.5.9.jar:1.5.9]

This is due to following code executed in TomcatMetrics:

FunctionTimer.builder("tomcat.global.request", mBeanServer,
                    s -> safeLong(() -> s.getAttribute(name, "requestCount")),
                    s -> safeDouble(() -> s.getAttribute(name, "processingTime")), TimeUnit.MILLISECONDS)
                    .tags(allTags)
                    .register(registry);

TimeGauge.builder("tomcat.global.request.max", mBeanServer, TimeUnit.MILLISECONDS,
                    s -> safeDouble(() -> s.getAttribute(name, "maxTime")))
                    .tags(allTags)
                    .register(registry);

First statement registers following metrics in Prometheus: - tomcat_global_request_count - tomcat_global_request_sum - tomcat_global_request_max

Second statement fails with message above. It affects also tomcat.servlet.request.max metric.

Proposed change is to rename metric in second statement to tomcat.servlet.request.maxtime

Source code to reproduce this issue: https://github.com/tjuchniewicz/spring-boot-tomcat-metrics-issue

Comment From: wilkinsona

Thanks for the analysis and suggested fix. TomcatMetrics is part of the Micrometer project. Can you please open a Micrometer issue so that the they can take a look?