Skip to content

Commit 0eea3c1

Browse files
committed
fix: restore legacy suffix compatibility
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
1 parent cd7d432 commit 0eea3c1

4 files changed

Lines changed: 135 additions & 13 deletions

File tree

prometheus-metrics-exposition-formats/src/main/java/io/prometheus/metrics/expositionformats/internal/PrometheusProtobufWriterImpl.java

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import io.prometheus.metrics.model.snapshots.MetricSnapshot;
2222
import io.prometheus.metrics.model.snapshots.MetricSnapshots;
2323
import io.prometheus.metrics.model.snapshots.NativeHistogramBuckets;
24+
import io.prometheus.metrics.model.snapshots.PrometheusNaming;
2425
import io.prometheus.metrics.model.snapshots.Quantiles;
2526
import io.prometheus.metrics.model.snapshots.SnapshotEscaper;
2627
import io.prometheus.metrics.model.snapshots.StateSetSnapshot;
@@ -82,7 +83,7 @@ public Metrics.MetricFamily convert(MetricSnapshot snapshot, EscapingScheme sche
8283
builder.addMetric(convert(data, scheme));
8384
}
8485
setMetadataUnlessEmpty(
85-
builder, snapshot.getMetadata(), null, Metrics.MetricType.GAUGE, scheme);
86+
builder, snapshot.getMetadata(), null, Metrics.MetricType.GAUGE, scheme, true);
8687
} else if (snapshot instanceof HistogramSnapshot) {
8788
HistogramSnapshot histogram = (HistogramSnapshot) snapshot;
8889
for (HistogramSnapshot.HistogramDataPointSnapshot data : histogram.getDataPoints()) {
@@ -290,25 +291,53 @@ private void setMetadataUnlessEmpty(
290291
@Nullable String nameSuffix,
291292
Metrics.MetricType type,
292293
EscapingScheme scheme) {
294+
setMetadataUnlessEmpty(builder, metadata, nameSuffix, type, scheme, false);
295+
}
296+
297+
private void setMetadataUnlessEmpty(
298+
Metrics.MetricFamily.Builder builder,
299+
MetricMetadata metadata,
300+
@Nullable String nameSuffix,
301+
Metrics.MetricType type,
302+
EscapingScheme scheme,
303+
boolean normalizeLegacyGaugeName) {
293304
if (builder.getMetricCount() == 0) {
294305
return;
295306
}
296-
if (nameSuffix == null) {
297-
builder.setName(SnapshotEscaper.getMetadataName(metadata, scheme));
298-
} else {
299-
String expositionBaseName = SnapshotEscaper.getExpositionBaseMetadataName(metadata, scheme);
300-
if (expositionBaseName.endsWith(nameSuffix)) {
301-
builder.setName(expositionBaseName);
302-
} else {
303-
builder.setName(SnapshotEscaper.getMetadataName(metadata, scheme) + nameSuffix);
304-
}
305-
}
307+
builder.setName(
308+
resolveMetricFamilyName(metadata, nameSuffix, scheme, normalizeLegacyGaugeName));
306309
if (metadata.getHelp() != null) {
307310
builder.setHelp(metadata.getHelp());
308311
}
309312
builder.setType(type);
310313
}
311314

315+
private String resolveMetricFamilyName(
316+
MetricMetadata metadata,
317+
@Nullable String nameSuffix,
318+
EscapingScheme scheme,
319+
boolean normalizeLegacyGaugeName) {
320+
if (normalizeLegacyGaugeName) {
321+
String originalName = metadata.getOriginalName();
322+
if (originalName.endsWith(".created")) {
323+
return PrometheusNaming.escapeName(
324+
originalName.substring(0, originalName.length() - ".created".length()), scheme);
325+
}
326+
if (originalName.endsWith(".total")) {
327+
return PrometheusNaming.escapeName(
328+
originalName.substring(0, originalName.length() - ".total".length()), scheme);
329+
}
330+
}
331+
if (nameSuffix == null) {
332+
return SnapshotEscaper.getMetadataName(metadata, scheme);
333+
}
334+
String expositionBaseName = SnapshotEscaper.getExpositionBaseMetadataName(metadata, scheme);
335+
if (expositionBaseName.endsWith(nameSuffix)) {
336+
return expositionBaseName;
337+
}
338+
return SnapshotEscaper.getMetadataName(metadata, scheme) + nameSuffix;
339+
}
340+
312341
private long getNativeCount(HistogramSnapshot.HistogramDataPointSnapshot data) {
313342
if (data.hasCount()) {
314343
return data.getCount();

prometheus-metrics-exposition-formats/src/test/java/io/prometheus/metrics/expositionformats/DuplicateNamesProtobufTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,28 @@ void testDifferentMetrics_producesSeparateMetricFamilies() throws IOException {
239239
assertThat(gaugeFamily.getMetric(0).getGauge().getValue()).isEqualTo(50.0);
240240
}
241241

242+
@Test
243+
void testLegacyGaugeNameWithDotTotal_usesBaseName() throws IOException {
244+
MetricSnapshots snapshots =
245+
MetricSnapshots.of(
246+
GaugeSnapshot.builder()
247+
.name("legacy.total")
248+
.dataPoint(GaugeSnapshot.GaugeDataPointSnapshot.builder().value(7).build())
249+
.build());
250+
ByteArrayOutputStream out = new ByteArrayOutputStream();
251+
PrometheusProtobufWriterImpl writer = new PrometheusProtobufWriterImpl();
252+
writer.write(out, snapshots, EscapingScheme.UNDERSCORE_ESCAPING);
253+
254+
List<Metrics.MetricFamily> metricFamilies = parseProtobufOutput(out);
255+
256+
assertThat(metricFamilies).hasSize(1);
257+
Metrics.MetricFamily family = metricFamilies.get(0);
258+
assertThat(family.getName()).isEqualTo("legacy");
259+
assertThat(family.getType()).isEqualTo(Metrics.MetricType.GAUGE);
260+
assertThat(family.getMetricCount()).isEqualTo(1);
261+
assertThat(family.getMetric(0).getGauge().getValue()).isEqualTo(7.0);
262+
}
263+
242264
private static MetricSnapshots getMetricSnapshots() {
243265
PrometheusRegistry registry = new PrometheusRegistry();
244266

prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,10 @@ private void writeCounter(Writer writer, CounterSnapshot snapshot, EscapingSchem
192192
private void writeGauge(Writer writer, GaugeSnapshot snapshot, EscapingScheme scheme)
193193
throws IOException {
194194
MetricMetadata metadata = snapshot.getMetadata();
195-
writeMetadata(writer, "", "gauge", metadata, scheme);
195+
String gaugeName = resolveLegacyGaugeName(metadata, scheme);
196+
writeMetadataWithFullName(writer, gaugeName, "gauge", metadata);
196197
for (GaugeSnapshot.GaugeDataPointSnapshot data : snapshot.getDataPoints()) {
197-
writeNameAndLabels(writer, getMetadataName(metadata, scheme), null, data.getLabels(), scheme);
198+
writeNameAndLabels(writer, gaugeName, null, data.getLabels(), scheme);
198199
writeDouble(writer, data.getValue());
199200
writeScrapeTimestampAndNewline(writer, data);
200201
}
@@ -475,6 +476,19 @@ private static String resolveBaseName(String fullName, String suffix) {
475476
return fullName;
476477
}
477478

479+
private static String resolveLegacyGaugeName(MetricMetadata metadata, EscapingScheme scheme) {
480+
String originalName = metadata.getOriginalName();
481+
if (originalName.endsWith(".created")) {
482+
return PrometheusNaming.escapeName(
483+
originalName.substring(0, originalName.length() - ".created".length()), scheme);
484+
}
485+
if (originalName.endsWith(".total")) {
486+
return PrometheusNaming.escapeName(
487+
originalName.substring(0, originalName.length() - ".total".length()), scheme);
488+
}
489+
return getMetadataName(metadata, scheme);
490+
}
491+
478492
private void writeEscapedHelp(Writer writer, String s) throws IOException {
479493
for (int i = 0; i < s.length(); i++) {
480494
char c = s.charAt(i);

prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/ExpositionFormatsTest.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,63 @@ void testGaugeWithDots() throws IOException {
666666
assertPrometheusProtobuf(prometheusProtobuf, gauge);
667667
}
668668

669+
@Test
670+
void testGaugeReservedSuffixCompatibilityOutsideOpenMetrics() throws IOException {
671+
GaugeSnapshot createdGauge =
672+
GaugeSnapshot.builder()
673+
.name("test3.created")
674+
.dataPoint(GaugeDataPointSnapshot.builder().value(3).build())
675+
.build();
676+
assertOpenMetricsText(
677+
"""
678+
# TYPE U__test3_2e_created gauge
679+
U__test3_2e_created 3.0
680+
# EOF
681+
""",
682+
createdGauge);
683+
assertPrometheusText(
684+
"""
685+
# TYPE test3 gauge
686+
test3 3.0
687+
""",
688+
createdGauge);
689+
assertPrometheusTextWithoutCreated(
690+
"""
691+
# TYPE test3 gauge
692+
test3 3.0
693+
""",
694+
createdGauge);
695+
assertPrometheusProtobuf(
696+
"name: \"test3\" type: GAUGE metric { gauge { value: 3.0 } }", createdGauge);
697+
698+
GaugeSnapshot totalGauge =
699+
GaugeSnapshot.builder()
700+
.name("test6.total")
701+
.dataPoint(GaugeDataPointSnapshot.builder().value(6).build())
702+
.build();
703+
assertOpenMetricsText(
704+
"""
705+
# TYPE U__test6_2e_total gauge
706+
U__test6_2e_total 6.0
707+
# EOF
708+
""",
709+
totalGauge);
710+
assertPrometheusText(
711+
"""
712+
# TYPE test6 gauge
713+
test6 6.0
714+
""",
715+
totalGauge);
716+
assertPrometheusTextWithoutCreated(
717+
"""
718+
# TYPE test6 gauge
719+
test6 6.0
720+
""",
721+
totalGauge);
722+
assertPrometheusProtobuf(
723+
"name: \"test6\" type: GAUGE metric { gauge { value: 6.0 } }", totalGauge);
724+
}
725+
669726
@Test
670727
void testGaugeUTF8() throws IOException {
671728
String prometheusText =

0 commit comments

Comments
 (0)