From 725928c1ffeccdec68a53417da5f1da90b69c43a Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Wed, 13 May 2026 16:26:07 +0000 Subject: [PATCH] refactor: use typed Prometheus family descriptors Signed-off-by: Gregor Zeitlinger --- .../prometheus/Otel2PrometheusConverter.java | 49 ++++++++++++++----- .../Otel2PrometheusConverterTest.java | 28 ++--------- 2 files changed, 43 insertions(+), 34 deletions(-) diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverter.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverter.java index 1ba2322a8b4..c380edf5376 100644 --- a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverter.java +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverter.java @@ -42,6 +42,7 @@ import io.prometheus.metrics.model.snapshots.InfoSnapshot; import io.prometheus.metrics.model.snapshots.InfoSnapshot.InfoDataPointSnapshot; import io.prometheus.metrics.model.snapshots.Labels; +import io.prometheus.metrics.model.snapshots.MetricFamilyDescriptor; import io.prometheus.metrics.model.snapshots.MetricMetadata; import io.prometheus.metrics.model.snapshots.MetricSnapshot; import io.prometheus.metrics.model.snapshots.MetricSnapshots; @@ -51,6 +52,7 @@ import io.prometheus.metrics.model.snapshots.SummarySnapshot; import io.prometheus.metrics.model.snapshots.SummarySnapshot.SummaryDataPointSnapshot; import io.prometheus.metrics.model.snapshots.Unit; +import io.prometheus.metrics.model.registry.MetricType; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -470,7 +472,7 @@ private Exemplar convertExemplar(double value, ExemplarData exemplar) { private InfoSnapshot makeTargetInfo(Resource resource) { return new InfoSnapshot( - new MetricMetadata("target"), + MetricFamilyDescriptor.info("target").build().getMetadata(), Collections.singletonList( new InfoDataPointSnapshot( convertAttributes( @@ -679,20 +681,22 @@ private static boolean isValidLegacyMetricChar(int codePoint, int index) { } private MetricMetadata convertMetadata(MetricData metricData, boolean isCounter) { + MetricType metricType = getMetricType(metricData, isCounter); switch (translationStrategy) { case UNDERSCORE_ESCAPING_WITH_SUFFIXES: - return convertMetadataEscapedWithSuffixes(metricData); + return convertMetadataEscapedWithSuffixes(metricData, metricType); case UNDERSCORE_ESCAPING_WITHOUT_SUFFIXES: return convertMetadataEscapedWithoutSuffixes(metricData); case NO_UTF8_ESCAPING_WITH_SUFFIXES: - return convertMetadataUtf8WithSuffixes(metricData, isCounter); + return convertMetadataUtf8WithSuffixes(metricData, metricType); case NO_TRANSLATION: return convertMetadataNoTranslation(metricData); } throw new IllegalStateException("Unknown strategy: " + translationStrategy); } - private static MetricMetadata convertMetadataEscapedWithSuffixes(MetricData metricData) { + private static MetricMetadata convertMetadataEscapedWithSuffixes( + MetricData metricData, MetricType metricType) { String originalName = metricData.getName(); String name = stripReservedMetricSuffixes(convertLegacyMetricName(originalName)); String help = metricData.getDescription(); @@ -701,7 +705,7 @@ private static MetricMetadata convertMetadataEscapedWithSuffixes(MetricData metr name = name + "_" + unit; } validateNormalizedMetricName(originalName, name); - return new MetricMetadata(name, name, name, help, unit); + return buildMetricFamilyDescriptor(metricType, name, help, unit).getMetadata(); } private static MetricMetadata convertMetadataEscapedWithoutSuffixes(MetricData metricData) { @@ -713,18 +717,14 @@ private static MetricMetadata convertMetadataEscapedWithoutSuffixes(MetricData m } private static MetricMetadata convertMetadataUtf8WithSuffixes( - MetricData metricData, boolean isCounter) { + MetricData metricData, MetricType metricType) { String name = metricData.getName(); String help = metricData.getDescription(); Unit unit = PrometheusUnitsHelper.convertUnit(metricData.getUnit()); if (unit != null && !name.endsWith(unit.toString())) { name = name + "_" + unit; } - String expositionBaseName = name; - if (isCounter && !expositionBaseName.endsWith("_total")) { - expositionBaseName = expositionBaseName + "_total"; - } - return new MetricMetadata(stripReservedMetricSuffixes(name), expositionBaseName, help, unit); + return buildMetricFamilyDescriptor(metricType, name, help, unit).getMetadata(); } private static MetricMetadata convertMetadataNoTranslation(MetricData metricData) { @@ -862,6 +862,33 @@ private static MetricMetadata mergeMetadata(MetricMetadata a, MetricMetadata b) return new MetricMetadata(name, help, unit); } + private static MetricType getMetricType(MetricData metricData, boolean isCounter) { + switch (metricData.getType()) { + case LONG_GAUGE: + case DOUBLE_GAUGE: + return MetricType.GAUGE; + case LONG_SUM: + case DOUBLE_SUM: + return isCounter ? MetricType.COUNTER : MetricType.GAUGE; + case HISTOGRAM: + case EXPONENTIAL_HISTOGRAM: + return MetricType.HISTOGRAM; + case SUMMARY: + return MetricType.SUMMARY; + } + throw new IllegalStateException("Unknown metric type: " + metricData.getType()); + } + + private static MetricFamilyDescriptor buildMetricFamilyDescriptor( + MetricType metricType, String name, @Nullable String help, @Nullable Unit unit) { + MetricFamilyDescriptor.Builder builder = + MetricFamilyDescriptor.of(metricType, name).help(help); + if (unit != null) { + builder.unit(unit); + } + return builder.build(); + } + private static String typeString(MetricSnapshot snapshot) { // Simple helper for a log message. return snapshot.getClass().getSimpleName().replace("Snapshot", "").toLowerCase(Locale.ENGLISH); diff --git a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverterTest.java b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverterTest.java index 1cfab0aafce..24f1cb35dd6 100644 --- a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverterTest.java +++ b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverterTest.java @@ -198,10 +198,7 @@ private static Stream metricMetadataArgs() { @ParameterizedTest @MethodSource("translationStrategyArgs") void metricMetadata_translationStrategy( - TranslationStrategy translationStrategy, - String expectedName, - String expectedExpositionBaseName, - String expectedOriginalName) { + TranslationStrategy translationStrategy, String expectedName) { Otel2PrometheusConverter converter = new Otel2PrometheusConverter( /* otelScopeLabelsEnabled= */ true, @@ -216,29 +213,14 @@ void metricMetadata_translationStrategy( MetricMetadata metadata = snapshots.get(0).getMetadata(); assertThat(metadata.getName()).isEqualTo(expectedName); - assertThat(metadata.getExpositionBaseName()).isEqualTo(expectedExpositionBaseName); - assertThat(metadata.getOriginalName()).isEqualTo(expectedOriginalName); } private static Stream translationStrategyArgs() { return Stream.of( - Arguments.of( - TranslationStrategy.UNDERSCORE_ESCAPING_WITH_SUFFIXES, - "sample_name_bytes", - "sample_name_bytes", - "sample_name_bytes"), - Arguments.of( - TranslationStrategy.UNDERSCORE_ESCAPING_WITHOUT_SUFFIXES, - "sample_name", - "sample_name", - "sample_name"), - Arguments.of( - TranslationStrategy.NO_UTF8_ESCAPING_WITH_SUFFIXES, - "sample.name_bytes", - "sample.name_bytes_total", - "sample.name_bytes_total"), - Arguments.of( - TranslationStrategy.NO_TRANSLATION, "sample.name", "sample.name", "sample.name")); + Arguments.of(TranslationStrategy.UNDERSCORE_ESCAPING_WITH_SUFFIXES, "sample_name_bytes"), + Arguments.of(TranslationStrategy.UNDERSCORE_ESCAPING_WITHOUT_SUFFIXES, "sample_name"), + Arguments.of(TranslationStrategy.NO_UTF8_ESCAPING_WITH_SUFFIXES, "sample.name_bytes"), + Arguments.of(TranslationStrategy.NO_TRANSLATION, "sample.name")); } @Test