From 74917b842ad8571e05031b0b9450f03e997ee93c Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 5 May 2026 15:03:15 +0000 Subject: [PATCH 1/2] fix: restore legacy suffix compatibility Signed-off-by: Gregor Zeitlinger --- .../PrometheusProtobufWriterImpl.java | 51 +++++++++++++---- .../DuplicateNamesProtobufTest.java | 22 +++++++ .../PrometheusTextFormatWriter.java | 19 ++++++- .../ExpositionFormatsTest.java | 57 +++++++++++++++++++ 4 files changed, 135 insertions(+), 14 deletions(-) diff --git a/prometheus-metrics-exposition-formats/src/main/java/io/prometheus/metrics/expositionformats/internal/PrometheusProtobufWriterImpl.java b/prometheus-metrics-exposition-formats/src/main/java/io/prometheus/metrics/expositionformats/internal/PrometheusProtobufWriterImpl.java index 5e58275ca..6dd0bbd31 100644 --- a/prometheus-metrics-exposition-formats/src/main/java/io/prometheus/metrics/expositionformats/internal/PrometheusProtobufWriterImpl.java +++ b/prometheus-metrics-exposition-formats/src/main/java/io/prometheus/metrics/expositionformats/internal/PrometheusProtobufWriterImpl.java @@ -21,6 +21,7 @@ import io.prometheus.metrics.model.snapshots.MetricSnapshot; import io.prometheus.metrics.model.snapshots.MetricSnapshots; import io.prometheus.metrics.model.snapshots.NativeHistogramBuckets; +import io.prometheus.metrics.model.snapshots.PrometheusNaming; import io.prometheus.metrics.model.snapshots.Quantiles; import io.prometheus.metrics.model.snapshots.SnapshotEscaper; import io.prometheus.metrics.model.snapshots.StateSetSnapshot; @@ -82,7 +83,7 @@ public Metrics.MetricFamily convert(MetricSnapshot snapshot, EscapingScheme sche builder.addMetric(convert(data, scheme)); } setMetadataUnlessEmpty( - builder, snapshot.getMetadata(), null, Metrics.MetricType.GAUGE, scheme); + builder, snapshot.getMetadata(), null, Metrics.MetricType.GAUGE, scheme, true); } else if (snapshot instanceof HistogramSnapshot) { HistogramSnapshot histogram = (HistogramSnapshot) snapshot; for (HistogramSnapshot.HistogramDataPointSnapshot data : histogram.getDataPoints()) { @@ -290,25 +291,53 @@ private void setMetadataUnlessEmpty( @Nullable String nameSuffix, Metrics.MetricType type, EscapingScheme scheme) { + setMetadataUnlessEmpty(builder, metadata, nameSuffix, type, scheme, false); + } + + private void setMetadataUnlessEmpty( + Metrics.MetricFamily.Builder builder, + MetricMetadata metadata, + @Nullable String nameSuffix, + Metrics.MetricType type, + EscapingScheme scheme, + boolean normalizeLegacyGaugeName) { if (builder.getMetricCount() == 0) { return; } - if (nameSuffix == null) { - builder.setName(SnapshotEscaper.getMetadataName(metadata, scheme)); - } else { - String expositionBaseName = SnapshotEscaper.getExpositionBaseMetadataName(metadata, scheme); - if (expositionBaseName.endsWith(nameSuffix)) { - builder.setName(expositionBaseName); - } else { - builder.setName(SnapshotEscaper.getMetadataName(metadata, scheme) + nameSuffix); - } - } + builder.setName( + resolveMetricFamilyName(metadata, nameSuffix, scheme, normalizeLegacyGaugeName)); if (metadata.getHelp() != null) { builder.setHelp(metadata.getHelp()); } builder.setType(type); } + private String resolveMetricFamilyName( + MetricMetadata metadata, + @Nullable String nameSuffix, + EscapingScheme scheme, + boolean normalizeLegacyGaugeName) { + if (normalizeLegacyGaugeName) { + String originalName = metadata.getOriginalName(); + if (originalName.endsWith(".created")) { + return PrometheusNaming.escapeName( + originalName.substring(0, originalName.length() - ".created".length()), scheme); + } + if (originalName.endsWith(".total")) { + return PrometheusNaming.escapeName( + originalName.substring(0, originalName.length() - ".total".length()), scheme); + } + } + if (nameSuffix == null) { + return SnapshotEscaper.getMetadataName(metadata, scheme); + } + String expositionBaseName = SnapshotEscaper.getExpositionBaseMetadataName(metadata, scheme); + if (expositionBaseName.endsWith(nameSuffix)) { + return expositionBaseName; + } + return SnapshotEscaper.getMetadataName(metadata, scheme) + nameSuffix; + } + private long getNativeCount(HistogramSnapshot.HistogramDataPointSnapshot data) { if (data.hasCount()) { return data.getCount(); diff --git a/prometheus-metrics-exposition-formats/src/test/java/io/prometheus/metrics/expositionformats/DuplicateNamesProtobufTest.java b/prometheus-metrics-exposition-formats/src/test/java/io/prometheus/metrics/expositionformats/DuplicateNamesProtobufTest.java index c5fc7bf34..ba0b653c0 100644 --- a/prometheus-metrics-exposition-formats/src/test/java/io/prometheus/metrics/expositionformats/DuplicateNamesProtobufTest.java +++ b/prometheus-metrics-exposition-formats/src/test/java/io/prometheus/metrics/expositionformats/DuplicateNamesProtobufTest.java @@ -239,6 +239,28 @@ void testDifferentMetrics_producesSeparateMetricFamilies() throws IOException { assertThat(gaugeFamily.getMetric(0).getGauge().getValue()).isEqualTo(50.0); } + @Test + void testLegacyGaugeNameWithDotTotal_usesBaseName() throws IOException { + MetricSnapshots snapshots = + MetricSnapshots.of( + GaugeSnapshot.builder() + .name("legacy.total") + .dataPoint(GaugeSnapshot.GaugeDataPointSnapshot.builder().value(7).build()) + .build()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + PrometheusProtobufWriterImpl writer = new PrometheusProtobufWriterImpl(); + writer.write(out, snapshots, EscapingScheme.UNDERSCORE_ESCAPING); + + List metricFamilies = parseProtobufOutput(out); + + assertThat(metricFamilies).hasSize(1); + Metrics.MetricFamily family = metricFamilies.get(0); + assertThat(family.getName()).isEqualTo("legacy"); + assertThat(family.getType()).isEqualTo(Metrics.MetricType.GAUGE); + assertThat(family.getMetricCount()).isEqualTo(1); + assertThat(family.getMetric(0).getGauge().getValue()).isEqualTo(7.0); + } + private static MetricSnapshots getMetricSnapshots() { PrometheusRegistry registry = new PrometheusRegistry(); diff --git a/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java b/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java index 29894b103..f20616a27 100644 --- a/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java +++ b/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java @@ -192,10 +192,10 @@ private void writeCounter(Writer writer, CounterSnapshot snapshot, EscapingSchem private void writeGauge(Writer writer, GaugeSnapshot snapshot, EscapingScheme scheme) throws IOException { MetricMetadata metadata = snapshot.getMetadata(); - writeMetadata(writer, null, "gauge", metadata, scheme); - String name = getMetadataName(metadata, scheme); + String gaugeName = resolveLegacyGaugeName(metadata, scheme); + writeMetadataWithFullName(writer, gaugeName, "gauge", metadata); for (GaugeSnapshot.GaugeDataPointSnapshot data : snapshot.getDataPoints()) { - writeNameAndLabels(writer, name, null, data.getLabels(), scheme); + writeNameAndLabels(writer, gaugeName, null, data.getLabels(), scheme); writeDouble(writer, data.getValue()); writeScrapeTimestampAndNewline(writer, data); } @@ -471,6 +471,19 @@ private static String resolveBaseName(String fullName, String suffix) { return fullName; } + private static String resolveLegacyGaugeName(MetricMetadata metadata, EscapingScheme scheme) { + String originalName = metadata.getOriginalName(); + if (originalName.endsWith(".created")) { + return PrometheusNaming.escapeName( + originalName.substring(0, originalName.length() - ".created".length()), scheme); + } + if (originalName.endsWith(".total")) { + return PrometheusNaming.escapeName( + originalName.substring(0, originalName.length() - ".total".length()), scheme); + } + return getMetadataName(metadata, scheme); + } + private void writeEscapedHelp(Writer writer, String s) throws IOException { for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); diff --git a/prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/ExpositionFormatsTest.java b/prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/ExpositionFormatsTest.java index 454b3ef7b..3deeb6b37 100644 --- a/prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/ExpositionFormatsTest.java +++ b/prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/ExpositionFormatsTest.java @@ -666,6 +666,63 @@ void testGaugeWithDots() throws IOException { assertPrometheusProtobuf(prometheusProtobuf, gauge); } + @Test + void testGaugeReservedSuffixCompatibilityOutsideOpenMetrics() throws IOException { + GaugeSnapshot createdGauge = + GaugeSnapshot.builder() + .name("test3.created") + .dataPoint(GaugeDataPointSnapshot.builder().value(3).build()) + .build(); + assertOpenMetricsText( + """ + # TYPE U__test3_2e_created gauge + U__test3_2e_created 3.0 + # EOF + """, + createdGauge); + assertPrometheusText( + """ + # TYPE test3 gauge + test3 3.0 + """, + createdGauge); + assertPrometheusTextWithoutCreated( + """ + # TYPE test3 gauge + test3 3.0 + """, + createdGauge); + assertPrometheusProtobuf( + "name: \"test3\" type: GAUGE metric { gauge { value: 3.0 } }", createdGauge); + + GaugeSnapshot totalGauge = + GaugeSnapshot.builder() + .name("test6.total") + .dataPoint(GaugeDataPointSnapshot.builder().value(6).build()) + .build(); + assertOpenMetricsText( + """ + # TYPE U__test6_2e_total gauge + U__test6_2e_total 6.0 + # EOF + """, + totalGauge); + assertPrometheusText( + """ + # TYPE test6 gauge + test6 6.0 + """, + totalGauge); + assertPrometheusTextWithoutCreated( + """ + # TYPE test6 gauge + test6 6.0 + """, + totalGauge); + assertPrometheusProtobuf( + "name: \"test6\" type: GAUGE metric { gauge { value: 6.0 } }", totalGauge); + } + @Test void testGaugeUTF8() throws IOException { String prometheusText = From b5b3def8540e445479e62467ab1be66a0e431501 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 12 May 2026 10:39:00 +0000 Subject: [PATCH 2/2] fix: normalize legacy gauge names from raw metadata Signed-off-by: Gregor Zeitlinger --- .../PrometheusProtobufWriterImpl.java | 39 ++++++++++++------- .../DuplicateNamesProtobufTest.java | 25 ++++++++++++ .../PrometheusTextFormatWriter.java | 21 +++------- .../ExpositionFormatsTest.java | 30 +++++++++++++- .../model/snapshots/SnapshotEscaper.java | 20 ++++++++++ 5 files changed, 103 insertions(+), 32 deletions(-) diff --git a/prometheus-metrics-exposition-formats/src/main/java/io/prometheus/metrics/expositionformats/internal/PrometheusProtobufWriterImpl.java b/prometheus-metrics-exposition-formats/src/main/java/io/prometheus/metrics/expositionformats/internal/PrometheusProtobufWriterImpl.java index 6dd0bbd31..9c3f74943 100644 --- a/prometheus-metrics-exposition-formats/src/main/java/io/prometheus/metrics/expositionformats/internal/PrometheusProtobufWriterImpl.java +++ b/prometheus-metrics-exposition-formats/src/main/java/io/prometheus/metrics/expositionformats/internal/PrometheusProtobufWriterImpl.java @@ -21,7 +21,6 @@ import io.prometheus.metrics.model.snapshots.MetricSnapshot; import io.prometheus.metrics.model.snapshots.MetricSnapshots; import io.prometheus.metrics.model.snapshots.NativeHistogramBuckets; -import io.prometheus.metrics.model.snapshots.PrometheusNaming; import io.prometheus.metrics.model.snapshots.Quantiles; import io.prometheus.metrics.model.snapshots.SnapshotEscaper; import io.prometheus.metrics.model.snapshots.StateSetSnapshot; @@ -51,7 +50,10 @@ public String toDebugString(MetricSnapshots metricSnapshots, EscapingScheme esca for (MetricSnapshot s : merged) { MetricSnapshot snapshot = SnapshotEscaper.escapeMetricSnapshot(s, escapingScheme); if (!snapshot.getDataPoints().isEmpty()) { - stringBuilder.append(TextFormat.printer().printToString(convert(snapshot, escapingScheme))); + stringBuilder.append( + TextFormat.printer() + .printToString( + convert(snapshot, s.getMetadata().getOriginalName(), escapingScheme))); } } return stringBuilder.toString(); @@ -65,12 +67,17 @@ public void write( for (MetricSnapshot s : merged) { MetricSnapshot snapshot = SnapshotEscaper.escapeMetricSnapshot(s, escapingScheme); if (!snapshot.getDataPoints().isEmpty()) { - convert(snapshot, escapingScheme).writeDelimitedTo(out); + convert(snapshot, s.getMetadata().getOriginalName(), escapingScheme).writeDelimitedTo(out); } } } public Metrics.MetricFamily convert(MetricSnapshot snapshot, EscapingScheme scheme) { + return convert(snapshot, snapshot.getMetadata().getOriginalName(), scheme); + } + + private Metrics.MetricFamily convert( + MetricSnapshot snapshot, String rawOriginalName, EscapingScheme scheme) { Metrics.MetricFamily.Builder builder = Metrics.MetricFamily.newBuilder(); if (snapshot instanceof CounterSnapshot) { for (CounterDataPointSnapshot data : ((CounterSnapshot) snapshot).getDataPoints()) { @@ -83,7 +90,13 @@ public Metrics.MetricFamily convert(MetricSnapshot snapshot, EscapingScheme sche builder.addMetric(convert(data, scheme)); } setMetadataUnlessEmpty( - builder, snapshot.getMetadata(), null, Metrics.MetricType.GAUGE, scheme, true); + builder, + snapshot.getMetadata(), + rawOriginalName, + null, + Metrics.MetricType.GAUGE, + scheme, + true); } else if (snapshot instanceof HistogramSnapshot) { HistogramSnapshot histogram = (HistogramSnapshot) snapshot; for (HistogramSnapshot.HistogramDataPointSnapshot data : histogram.getDataPoints()) { @@ -291,12 +304,14 @@ private void setMetadataUnlessEmpty( @Nullable String nameSuffix, Metrics.MetricType type, EscapingScheme scheme) { - setMetadataUnlessEmpty(builder, metadata, nameSuffix, type, scheme, false); + setMetadataUnlessEmpty( + builder, metadata, metadata.getOriginalName(), nameSuffix, type, scheme, false); } private void setMetadataUnlessEmpty( Metrics.MetricFamily.Builder builder, MetricMetadata metadata, + String rawOriginalName, @Nullable String nameSuffix, Metrics.MetricType type, EscapingScheme scheme, @@ -305,7 +320,8 @@ private void setMetadataUnlessEmpty( return; } builder.setName( - resolveMetricFamilyName(metadata, nameSuffix, scheme, normalizeLegacyGaugeName)); + resolveMetricFamilyName( + metadata, rawOriginalName, nameSuffix, scheme, normalizeLegacyGaugeName)); if (metadata.getHelp() != null) { builder.setHelp(metadata.getHelp()); } @@ -314,19 +330,12 @@ private void setMetadataUnlessEmpty( private String resolveMetricFamilyName( MetricMetadata metadata, + String rawOriginalName, @Nullable String nameSuffix, EscapingScheme scheme, boolean normalizeLegacyGaugeName) { if (normalizeLegacyGaugeName) { - String originalName = metadata.getOriginalName(); - if (originalName.endsWith(".created")) { - return PrometheusNaming.escapeName( - originalName.substring(0, originalName.length() - ".created".length()), scheme); - } - if (originalName.endsWith(".total")) { - return PrometheusNaming.escapeName( - originalName.substring(0, originalName.length() - ".total".length()), scheme); - } + return SnapshotEscaper.getLegacyGaugeName(metadata, rawOriginalName, scheme); } if (nameSuffix == null) { return SnapshotEscaper.getMetadataName(metadata, scheme); diff --git a/prometheus-metrics-exposition-formats/src/test/java/io/prometheus/metrics/expositionformats/DuplicateNamesProtobufTest.java b/prometheus-metrics-exposition-formats/src/test/java/io/prometheus/metrics/expositionformats/DuplicateNamesProtobufTest.java index ba0b653c0..553d84af5 100644 --- a/prometheus-metrics-exposition-formats/src/test/java/io/prometheus/metrics/expositionformats/DuplicateNamesProtobufTest.java +++ b/prometheus-metrics-exposition-formats/src/test/java/io/prometheus/metrics/expositionformats/DuplicateNamesProtobufTest.java @@ -239,6 +239,21 @@ void testDifferentMetrics_producesSeparateMetricFamilies() throws IOException { assertThat(gaugeFamily.getMetric(0).getGauge().getValue()).isEqualTo(50.0); } + @Test + void testLegacyGaugeNameWithAlternateEscapingSchemes_usesBaseName() throws IOException { + MetricSnapshots snapshots = + MetricSnapshots.of( + GaugeSnapshot.builder() + .name("legacy.name.total") + .dataPoint(GaugeSnapshot.GaugeDataPointSnapshot.builder().value(7).build()) + .build()); + + assertThat(getSingleMetricFamilyName(snapshots, EscapingScheme.DOTS_ESCAPING)) + .isEqualTo("legacy_dot_name"); + assertThat(getSingleMetricFamilyName(snapshots, EscapingScheme.VALUE_ENCODING_ESCAPING)) + .isEqualTo("U__legacy_2e_name"); + } + @Test void testLegacyGaugeNameWithDotTotal_usesBaseName() throws IOException { MetricSnapshots snapshots = @@ -261,6 +276,16 @@ void testLegacyGaugeNameWithDotTotal_usesBaseName() throws IOException { assertThat(family.getMetric(0).getGauge().getValue()).isEqualTo(7.0); } + private static String getSingleMetricFamilyName( + MetricSnapshots snapshots, EscapingScheme escapingScheme) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + PrometheusProtobufWriterImpl writer = new PrometheusProtobufWriterImpl(); + writer.write(out, snapshots, escapingScheme); + List metricFamilies = parseProtobufOutput(out); + assertThat(metricFamilies).hasSize(1); + return metricFamilies.get(0).getName(); + } + private static MetricSnapshots getMetricSnapshots() { PrometheusRegistry registry = new PrometheusRegistry(); diff --git a/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java b/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java index f20616a27..8d9643378 100644 --- a/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java +++ b/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java @@ -8,6 +8,7 @@ import static io.prometheus.metrics.expositionformats.TextFormatUtil.writePrometheusTimestamp; import static io.prometheus.metrics.model.snapshots.SnapshotEscaper.escapeMetricSnapshot; import static io.prometheus.metrics.model.snapshots.SnapshotEscaper.getExpositionBaseMetadataName; +import static io.prometheus.metrics.model.snapshots.SnapshotEscaper.getLegacyGaugeName; import static io.prometheus.metrics.model.snapshots.SnapshotEscaper.getMetadataName; import static io.prometheus.metrics.model.snapshots.SnapshotEscaper.getSnapshotLabelName; @@ -123,7 +124,7 @@ public void write(OutputStream out, MetricSnapshots metricSnapshots, EscapingSch if (snapshot instanceof CounterSnapshot) { writeCounter(writer, (CounterSnapshot) snapshot, scheme); } else if (snapshot instanceof GaugeSnapshot) { - writeGauge(writer, (GaugeSnapshot) snapshot, scheme); + writeGauge(writer, (GaugeSnapshot) snapshot, s.getMetadata().getOriginalName(), scheme); } else if (snapshot instanceof HistogramSnapshot) { writeHistogram(writer, (HistogramSnapshot) snapshot, scheme); } else if (snapshot instanceof SummarySnapshot) { @@ -189,10 +190,11 @@ private void writeCounter(Writer writer, CounterSnapshot snapshot, EscapingSchem } } - private void writeGauge(Writer writer, GaugeSnapshot snapshot, EscapingScheme scheme) + private void writeGauge( + Writer writer, GaugeSnapshot snapshot, String rawOriginalName, EscapingScheme scheme) throws IOException { MetricMetadata metadata = snapshot.getMetadata(); - String gaugeName = resolveLegacyGaugeName(metadata, scheme); + String gaugeName = getLegacyGaugeName(metadata, rawOriginalName, scheme); writeMetadataWithFullName(writer, gaugeName, "gauge", metadata); for (GaugeSnapshot.GaugeDataPointSnapshot data : snapshot.getDataPoints()) { writeNameAndLabels(writer, gaugeName, null, data.getLabels(), scheme); @@ -471,19 +473,6 @@ private static String resolveBaseName(String fullName, String suffix) { return fullName; } - private static String resolveLegacyGaugeName(MetricMetadata metadata, EscapingScheme scheme) { - String originalName = metadata.getOriginalName(); - if (originalName.endsWith(".created")) { - return PrometheusNaming.escapeName( - originalName.substring(0, originalName.length() - ".created".length()), scheme); - } - if (originalName.endsWith(".total")) { - return PrometheusNaming.escapeName( - originalName.substring(0, originalName.length() - ".total".length()), scheme); - } - return getMetadataName(metadata, scheme); - } - private void writeEscapedHelp(Writer writer, String s) throws IOException { for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); diff --git a/prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/ExpositionFormatsTest.java b/prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/ExpositionFormatsTest.java index 3deeb6b37..c875b94b0 100644 --- a/prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/ExpositionFormatsTest.java +++ b/prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/ExpositionFormatsTest.java @@ -666,6 +666,29 @@ void testGaugeWithDots() throws IOException { assertPrometheusProtobuf(prometheusProtobuf, gauge); } + @Test + void testGaugeReservedSuffixCompatibilityWithAlternateEscapingSchemes() throws IOException { + GaugeSnapshot totalGauge = + GaugeSnapshot.builder() + .name("legacy.name.total") + .dataPoint(GaugeDataPointSnapshot.builder().value(6).build()) + .build(); + assertPrometheusText( + """ + # TYPE legacy_dot_name gauge + legacy_dot_name 6.0 + """, + totalGauge, + EscapingScheme.DOTS_ESCAPING); + assertPrometheusText( + """ + # TYPE U__legacy_2e_name gauge + U__legacy_2e_name 6.0 + """, + totalGauge, + EscapingScheme.VALUE_ENCODING_ESCAPING); + } + @Test void testGaugeReservedSuffixCompatibilityOutsideOpenMetrics() throws IOException { GaugeSnapshot createdGauge = @@ -3145,9 +3168,14 @@ private void assertOpenMetricsTextWithoutCreated(String expected, MetricSnapshot } private void assertPrometheusText(String expected, MetricSnapshot snapshot) throws IOException { + assertPrometheusText(expected, snapshot, EscapingScheme.UNDERSCORE_ESCAPING); + } + + private void assertPrometheusText( + String expected, MetricSnapshot snapshot, EscapingScheme escapingScheme) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); getPrometheusWriter(PrometheusTextFormatWriter.builder().setIncludeCreatedTimestamps(true)) - .write(out, MetricSnapshots.of(snapshot), EscapingScheme.UNDERSCORE_ESCAPING); + .write(out, MetricSnapshots.of(snapshot), escapingScheme); assertThat(out).hasToString(expected); } diff --git a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/SnapshotEscaper.java b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/SnapshotEscaper.java index c3336af17..b8c887f01 100644 --- a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/SnapshotEscaper.java +++ b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/SnapshotEscaper.java @@ -113,6 +113,26 @@ public static String getOriginalMetadataName(MetricMetadata metadata, EscapingSc } } + public static String getLegacyGaugeName( + MetricMetadata metadata, String rawOriginalName, EscapingScheme scheme) { + String legacyGaugeBaseName = stripLegacyGaugeSuffix(rawOriginalName); + if (legacyGaugeBaseName != null) { + return PrometheusNaming.escapeName(legacyGaugeBaseName, scheme); + } + return getMetadataName(metadata, scheme); + } + + @Nullable + private static String stripLegacyGaugeSuffix(String rawOriginalName) { + if (rawOriginalName.endsWith(".created")) { + return rawOriginalName.substring(0, rawOriginalName.length() - ".created".length()); + } + if (rawOriginalName.endsWith(".total")) { + return rawOriginalName.substring(0, rawOriginalName.length() - ".total".length()); + } + return null; + } + public static Labels escapeLabels(Labels labels, EscapingScheme scheme) { Labels.Builder outLabelsBuilder = Labels.builder();