diff --git a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistry.java b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistry.java index aac4e9103b..2f47eb1167 100644 --- a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistry.java +++ b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistry.java @@ -32,6 +32,7 @@ import io.prometheus.metrics.model.snapshots.CounterSnapshot.CounterDataPointSnapshot; import io.prometheus.metrics.model.snapshots.GaugeSnapshot.GaugeDataPointSnapshot; import io.prometheus.metrics.model.snapshots.HistogramSnapshot.HistogramDataPointSnapshot; +import io.prometheus.metrics.model.snapshots.InfoSnapshot.InfoDataPointSnapshot; import io.prometheus.metrics.model.snapshots.SummarySnapshot.SummaryDataPointSnapshot; import java.io.ByteArrayOutputStream; @@ -310,10 +311,20 @@ protected io.micrometer.core.instrument.Gauge newGauge(Meter.Id id, @Nullabl Gauge gauge = new DefaultGauge<>(id, obj, valueFunction); applyToCollector(id, (collector) -> { List tagValues = tagValues(id); - collector.add(tagValues, - (conventionName, tagKeys) -> Stream.of(new MicrometerCollector.Family<>(conventionName, - family -> new GaugeSnapshot(family.metadata, family.dataPointSnapshots), getMetadata(id), - new GaugeDataPointSnapshot(gauge.value(), Labels.of(tagKeys, tagValues), null)))); + if (id.getName().endsWith(".info")) { + collector + .add(tagValues, + (conventionName, tagKeys) -> Stream.of(new MicrometerCollector.Family<>(conventionName, + family -> new InfoSnapshot(family.metadata, family.dataPointSnapshots), + getMetadata(id), new InfoDataPointSnapshot(Labels.of(tagKeys, tagValues))))); + } + else { + collector.add(tagValues, + (conventionName, tagKeys) -> Stream.of(new MicrometerCollector.Family<>(conventionName, + family -> new GaugeSnapshot(family.metadata, family.dataPointSnapshots), + getMetadata(id), + new GaugeDataPointSnapshot(gauge.value(), Labels.of(tagKeys, tagValues), null)))); + } }); return gauge; } diff --git a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusNamingConvention.java b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusNamingConvention.java index 1f96185800..9460c8b84b 100644 --- a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusNamingConvention.java +++ b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusNamingConvention.java @@ -18,8 +18,7 @@ import io.micrometer.common.lang.Nullable; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.config.NamingConvention; - -import java.util.regex.Pattern; +import io.prometheus.metrics.model.snapshots.PrometheusNaming; /** * See https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels for a @@ -29,10 +28,6 @@ */ public class PrometheusNamingConvention implements NamingConvention { - private static final Pattern nameChars = Pattern.compile("[^a-zA-Z0-9_:]"); - - private static final Pattern tagKeyChars = Pattern.compile("[^a-zA-Z0-9_]"); - private final String timerSuffix; public PrometheusNamingConvention() { @@ -45,9 +40,6 @@ public PrometheusNamingConvention(String timerSuffix) { /** * Names are snake-cased. They contain a base unit suffix when applicable. - *

- * Names may contain ASCII letters and digits, as well as underscores and colons. They - * must match the regex [a-zA-Z_:][a-zA-Z0-9_:]* */ @Override public String name(String name, Meter.Type type, @Nullable String baseUnit) { @@ -81,11 +73,7 @@ else if (!conventionName.endsWith("_seconds")) { break; } - String sanitized = nameChars.matcher(conventionName).replaceAll("_"); - if (!Character.isLetter(sanitized.charAt(0))) { - sanitized = "m_" + sanitized; - } - return sanitized; + return PrometheusNaming.sanitizeMetricName(conventionName); } /** @@ -95,13 +83,7 @@ else if (!conventionName.endsWith("_seconds")) { */ @Override public String tagKey(String key) { - String conventionKey = NamingConvention.snakeCase.tagKey(key); - - String sanitized = tagKeyChars.matcher(conventionKey).replaceAll("_"); - if (!Character.isLetter(sanitized.charAt(0))) { - sanitized = "m_" + sanitized; - } - return sanitized; + return PrometheusNaming.sanitizeLabelName(key); } } diff --git a/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java b/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java index 2d0d8fd40d..f3b5e06257 100644 --- a/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java +++ b/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java @@ -16,14 +16,12 @@ package io.micrometer.prometheusmetrics; import io.micrometer.core.Issue; -import io.micrometer.core.instrument.Timer; import io.micrometer.core.instrument.*; import io.micrometer.core.instrument.binder.BaseUnits; +import io.micrometer.core.instrument.binder.jvm.JvmInfoMetrics; import io.micrometer.core.instrument.composite.CompositeMeterRegistry; import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; import io.micrometer.core.instrument.distribution.HistogramSnapshot; -import io.micrometer.prometheusmetrics.PrometheusConfig; -import io.micrometer.prometheusmetrics.PrometheusMeterRegistry; import io.prometheus.metrics.model.registry.PrometheusRegistry; import io.prometheus.metrics.model.snapshots.*; import io.prometheus.metrics.tracer.common.SpanContext; @@ -32,7 +30,10 @@ import org.junit.jupiter.api.Test; import java.time.Duration; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -103,6 +104,37 @@ void quantiles() { assertThat(prometheusRegistry.scrape()).has(withNameAndQuantile("ds")); } + @Test + void invalidMeterNameSuffixesShouldBeRemovedForMetadata() { + new JvmInfoMetrics().bindTo(registry); + Gauge.builder("test1.info", () -> 1).register(registry); + Counter.builder("test2.total").register(registry).increment(2); + Gauge.builder("test3.created", () -> 3).register(registry); + Counter.builder("test4.bucket").register(registry).increment(4); + + Counter.builder("test5.info").register(registry).increment(5); + Gauge.builder("test6.total", () -> 6).register(registry); + + String scrapeResult = registry.scrape(); + + assertThat(scrapeResult).contains("# HELP test1_info") + .contains("# TYPE test1_info gauge") + .contains("test1_info 1"); + assertThat(scrapeResult).contains("# HELP test2_total") + .contains("# TYPE test2_total counter") + .contains("test2_total 2.0"); + assertThat(scrapeResult).contains("# HELP jvm_info").contains("# TYPE jvm_info gauge").contains("jvm_info{"); + assertThat(scrapeResult).contains("# HELP test3").contains("# TYPE test3 gauge").contains("test3 3"); + assertThat(scrapeResult).contains("# HELP test4_total") + .contains("# TYPE test4_total counter") + .contains("test4_total 4.0"); + + assertThat(scrapeResult).contains("# HELP test5_total") + .contains("# TYPE test5_total counter") + .contains("test5_total 5.0"); + assertThat(scrapeResult).contains("# HELP test6").contains("# TYPE test6 gauge").contains("test6 6"); + } + @Issue("#27") @DisplayName("custom distribution summaries respect varying tags") @Test diff --git a/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusNamingConventionTest.java b/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusNamingConventionTest.java index 8ba28bee9e..34934b8949 100644 --- a/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusNamingConventionTest.java +++ b/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusNamingConventionTest.java @@ -30,12 +30,12 @@ class PrometheusNamingConventionTest { @Test void formatName() { - assertThat(convention.name("123abc/{:id}水", Meter.Type.GAUGE)).startsWith("m_123abc__:id__"); + assertThat(convention.name("123abc/{:id}水", Meter.Type.GAUGE)).startsWith("_23abc__:id__"); } @Test void formatTagKey() { - assertThat(convention.tagKey("123abc/{:id}水")).startsWith("m_123abc___id__"); + assertThat(convention.tagKey("123abc/{:id}水")).startsWith("_23abc___id__"); } @Test