From 6a89d7eff3039eeca39c44d9944c46d3ac1ed583 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 16:10:06 +0000 Subject: [PATCH 01/10] fix(deps): update prometheusserverversion to v1.4.1 --- dependencyManagement/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index 5a03fa847e9..8b3477f17e2 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -15,7 +15,7 @@ val jmhVersion = "1.37" val mockitoVersion = "4.11.0" val slf4jVersion = "2.0.17" val opencensusVersion = "0.31.1" -val prometheusServerVersion = "1.3.10" +val prometheusServerVersion = "1.4.1" val armeriaVersion = "1.33.1" val junitVersion = "5.13.4" val okhttpVersion = "5.1.0" From 28ae2276dae41441001d6f0442ca62b6df2174a9 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 18 Aug 2025 19:03:39 +0200 Subject: [PATCH 02/10] fix import --- .../exporter/prometheus/PrometheusHttpServerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java index 2b1f27b8a3e..0ff651133fc 100644 --- a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java +++ b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java @@ -46,7 +46,7 @@ import io.opentelemetry.sdk.resources.Resource; import io.prometheus.metrics.exporter.httpserver.HTTPServer; import io.prometheus.metrics.exporter.httpserver.MetricsHandler; -import io.prometheus.metrics.expositionformats.generated.com_google_protobuf_4_31_1.Metrics; +import io.prometheus.metrics.expositionformats.generated.com_google_protobuf_4_32_0.Metrics; import io.prometheus.metrics.model.registry.PrometheusRegistry; import java.io.ByteArrayInputStream; import java.io.IOException; From a5b2666fc6136fcf6de3cd91fef8fd93d26b869a Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 19 Aug 2025 12:48:05 +0200 Subject: [PATCH 03/10] comment --- .../exporter/prometheus/Otel2PrometheusConverter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 afa04a81394..1bed9718bef 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 @@ -555,7 +555,8 @@ private static MetricMetadata convertMetadata(MetricData metricData) { if (unit != null && !name.endsWith(unit.toString())) { name = name + "_" + unit; } - // Repeated __ are not allowed according to spec, although this is allowed in prometheus + // Repeated __ are discouraged according to spec, although this is allowed in prometheus, see + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/compatibility/prometheus_and_openmetrics.md#metric-metadata-1 while (name.contains("__")) { name = name.replace("__", "_"); } From 4746088ba91b49edc5376a7661bbcd092eaf6651 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 25 Aug 2025 19:17:56 +0200 Subject: [PATCH 04/10] add a flag to avoid a breaking change --- .../prometheus/Otel2PrometheusConverter.java | 21 +++-- .../prometheus/PrometheusHttpServer.java | 7 +- .../PrometheusHttpServerBuilder.java | 27 +++++++ .../prometheus/PrometheusMetricReader.java | 9 ++- .../internal/PrometheusComponentProvider.java | 4 +- .../PrometheusMetricReaderProvider.java | 3 + .../Otel2PrometheusConverterTest.java | 79 ++++++++++++++++--- .../PrometheusMetricReaderTest.java | 14 +++- 8 files changed, 140 insertions(+), 24 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 1bed9718bef..52e70976e43 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 @@ -5,6 +5,7 @@ package io.opentelemetry.exporter.prometheus; +import static io.prometheus.metrics.model.snapshots.PrometheusNaming.prometheusName; import static io.prometheus.metrics.model.snapshots.PrometheusNaming.sanitizeLabelName; import static io.prometheus.metrics.model.snapshots.PrometheusNaming.sanitizeMetricName; import static java.util.Objects.requireNonNull; @@ -91,6 +92,8 @@ final class Otel2PrometheusConverter { */ private final Map>> resourceAttributesToAllowedKeysCache; + private final boolean utf8SupportEnabled; + /** * Constructor with feature flag parameter. * @@ -100,13 +103,16 @@ final class Otel2PrometheusConverter { * matching this predicate will be added as labels on each exported metric */ Otel2PrometheusConverter( - boolean otelScopeEnabled, @Nullable Predicate allowedResourceAttributesFilter) { + boolean otelScopeEnabled, + @Nullable Predicate allowedResourceAttributesFilter, + boolean utf8SupportEnabled) { this.otelScopeEnabled = otelScopeEnabled; this.allowedResourceAttributesFilter = allowedResourceAttributesFilter; this.resourceAttributesToAllowedKeysCache = allowedResourceAttributesFilter != null ? new ConcurrentHashMap<>() : Collections.emptyMap(); + this.utf8SupportEnabled = utf8SupportEnabled; } MetricSnapshots convert(@Nullable Collection metricDataCollection) { @@ -457,8 +463,8 @@ private InfoSnapshot makeScopeInfo(Set scopes) { * Convert OpenTelemetry attributes to Prometheus labels. * * @param resource optional resource (attributes) to be converted. - * @param scope will be converted to {@code otel_scope_*} labels if {@code otelScopeEnabled} is - * {@code true}. + * @param scope that will be converted to {@code otel_scope_*} labels if {@code otelScopeEnabled} + * is {@code true}. * @param attributes the attributes to be converted. * @param additionalAttributes optional list of key/value pairs, may be empty. */ @@ -548,8 +554,13 @@ private List> filterAllowedResourceAttributeKeys(@Nullable Resou return allowedAttributeKeys; } - private static MetricMetadata convertMetadata(MetricData metricData) { - String name = sanitizeMetricName(metricData.getName()); + private MetricMetadata convertMetadata(MetricData metricData) { + String name = metricData.getName(); + if (!utf8SupportEnabled) { + name = prometheusName(name); + } + name = sanitizeMetricName(name); + String help = metricData.getDescription(); Unit unit = PrometheusUnitsHelper.convertUnit(metricData.getUnit()); if (unit != null && !name.endsWith(unit.toString())) { diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServer.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServer.java index e7b28e42acf..b3021dc9940 100644 --- a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServer.java +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServer.java @@ -43,6 +43,7 @@ public final class PrometheusHttpServer implements MetricReader { private final String host; private final int port; private final boolean otelScopeEnabled; + private final boolean utf8SupportEnabled; @Nullable private final Predicate allowedResourceAttributesFilter; private final MemoryMode memoryMode; private final DefaultAggregationSelector defaultAggregationSelector; @@ -73,6 +74,7 @@ public static PrometheusHttpServerBuilder builder() { @Nullable ExecutorService executor, PrometheusRegistry prometheusRegistry, boolean otelScopeEnabled, + boolean utf8SupportEnabled, @Nullable Predicate allowedResourceAttributesFilter, MemoryMode memoryMode, @Nullable HttpHandler defaultHandler, @@ -81,12 +83,14 @@ public static PrometheusHttpServerBuilder builder() { this.host = host; this.port = port; this.otelScopeEnabled = otelScopeEnabled; + this.utf8SupportEnabled = utf8SupportEnabled; this.allowedResourceAttributesFilter = allowedResourceAttributesFilter; this.memoryMode = memoryMode; this.defaultAggregationSelector = defaultAggregationSelector; this.builder = builder; this.prometheusMetricReader = - new PrometheusMetricReader(otelScopeEnabled, allowedResourceAttributesFilter); + new PrometheusMetricReader( + otelScopeEnabled, allowedResourceAttributesFilter, utf8SupportEnabled); this.prometheusRegistry = prometheusRegistry; prometheusRegistry.register(prometheusMetricReader); // When memory mode is REUSABLE_DATA, concurrent reads lead to data corruption. To prevent this, @@ -172,6 +176,7 @@ public String toString() { joiner.add("host=" + host); joiner.add("port=" + port); joiner.add("otelScopeEnabled=" + otelScopeEnabled); + joiner.add("utf8SupportEnabled=" + utf8SupportEnabled); joiner.add("allowedResourceAttributesFilter=" + allowedResourceAttributesFilter); joiner.add("memoryMode=" + memoryMode); joiner.add( diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerBuilder.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerBuilder.java index 9487ae61583..e2842be0411 100644 --- a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerBuilder.java +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerBuilder.java @@ -31,6 +31,7 @@ public final class PrometheusHttpServerBuilder { private int port = DEFAULT_PORT; private PrometheusRegistry prometheusRegistry = new PrometheusRegistry(); private boolean otelScopeEnabled = true; + private boolean utf8SupportEnabled = false; @Nullable private Predicate allowedResourceAttributesFilter; @Nullable private ExecutorService executor; private MemoryMode memoryMode = DEFAULT_MEMORY_MODE; @@ -46,6 +47,7 @@ public final class PrometheusHttpServerBuilder { this.port = builder.port; this.prometheusRegistry = builder.prometheusRegistry; this.otelScopeEnabled = builder.otelScopeEnabled; + this.utf8SupportEnabled = builder.utf8SupportEnabled; this.allowedResourceAttributesFilter = builder.allowedResourceAttributesFilter; this.executor = builder.executor; this.memoryMode = builder.memoryMode; @@ -90,6 +92,30 @@ public PrometheusHttpServerBuilder setOtelScopeEnabled(boolean otelScopeEnabled) return this; } + /** + * Set if UTF-8 support is enabled. + * + *

If set to {@code true}, the exporter will pass metric names and labels unchanged to the + * prometheus client library, which supports UTF-8. + * + *

UTF-8 will only be seen in the exported metrics if the prometheus server signals support for + * UTF-8 + * + *

Therefore, it's safe to always set this setting to {@code true} if you're not affected by + * following change in behavior: + * + *

If set to {@code true}, multiple non-legacy characters (e.g. %%) in a row will + * not be replaced with a single underscore as recommended in the Prometheus + * conversion specification. + */ + @SuppressWarnings("UnusedReturnValue") + public PrometheusHttpServerBuilder setUtf8SupportEnabled(boolean utf8SupportEnabled) { + this.utf8SupportEnabled = utf8SupportEnabled; + return this; + } + /** * Set if the resource attributes should be added as labels on each exported metric. * @@ -177,6 +203,7 @@ public PrometheusHttpServer build() { executor, prometheusRegistry, otelScopeEnabled, + utf8SupportEnabled, allowedResourceAttributesFilter, memoryMode, defaultHandler, diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReader.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReader.java index b51c83ab6f7..258c9531de4 100644 --- a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReader.java +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReader.java @@ -29,11 +29,14 @@ public class PrometheusMetricReader implements MetricReader, MultiCollector { private final Otel2PrometheusConverter converter; // TODO: refactor to public static create or builder pattern to align with project style - /** See {@link Otel2PrometheusConverter#Otel2PrometheusConverter(boolean, Predicate)}. */ + /** See {@link Otel2PrometheusConverter#Otel2PrometheusConverter(boolean, Predicate, boolean)}. */ public PrometheusMetricReader( - boolean otelScopeEnabled, @Nullable Predicate allowedResourceAttributesFilter) { + boolean otelScopeEnabled, + @Nullable Predicate allowedResourceAttributesFilter, + boolean utf8SupportEnabled) { this.converter = - new Otel2PrometheusConverter(otelScopeEnabled, allowedResourceAttributesFilter); + new Otel2PrometheusConverter( + otelScopeEnabled, allowedResourceAttributesFilter, utf8SupportEnabled); } @Override diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusComponentProvider.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusComponentProvider.java index 25d069db7a8..967384810d3 100644 --- a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusComponentProvider.java +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusComponentProvider.java @@ -33,7 +33,9 @@ public String getName() { @Override public MetricReader create(DeclarativeConfigProperties config) { - PrometheusHttpServerBuilder prometheusBuilder = PrometheusHttpServer.builder(); + PrometheusHttpServerBuilder prometheusBuilder = + PrometheusHttpServer.builder() + .setUtf8SupportEnabled(true); // we can accept a breaking change in declarative config Integer port = config.getInt("port"); if (port != null) { diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusMetricReaderProvider.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusMetricReaderProvider.java index 4c60d3def19..eab9de0fb0f 100644 --- a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusMetricReaderProvider.java +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusMetricReaderProvider.java @@ -33,6 +33,9 @@ public MetricReader createMetricReader(ConfigProperties config) { prometheusBuilder.setHost(host); } + prometheusBuilder.setUtf8SupportEnabled( + config.getBoolean("otel.exporter.prometheus.utf8", false)); + ExporterBuilderUtil.configureExporterMemoryMode(config, prometheusBuilder::setMemoryMode); String defaultHistogramAggregation = 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 4e718c78629..2afe51c28b3 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 @@ -37,6 +37,7 @@ import io.opentelemetry.sdk.metrics.internal.data.ImmutableSummaryData; import io.opentelemetry.sdk.metrics.internal.data.ImmutableSummaryPointData; import io.opentelemetry.sdk.resources.Resource; +import io.prometheus.metrics.config.EscapingScheme; import io.prometheus.metrics.expositionformats.ExpositionFormats; import io.prometheus.metrics.model.snapshots.CounterSnapshot; import io.prometheus.metrics.model.snapshots.Labels; @@ -68,24 +69,77 @@ class Otel2PrometheusConverterTest { "(.|\\n)*# HELP (?.*)\n# TYPE (?.*)\n(?.*)\\{" + "otel_scope_foo=\"bar\",otel_scope_name=\"scope\"," + "otel_scope_schema_url=\"schemaUrl\",otel_scope_version=\"version\"}(.|\\n)*"); + private static final Pattern ESCAPE_PATTERN = + Pattern.compile( + "(.|\\n)*# HELP (?.*)\n# TYPE (?.*)\n\\{\"(?.*)\"," + + "otel_scope_foo=\"bar\",otel_scope_name=\"scope\"," + + "otel_scope_schema_url=\"schemaUrl\",otel_scope_version=\"version\"}(.|\\n)*"); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private final Otel2PrometheusConverter converter = - new Otel2PrometheusConverter(true, /* allowedResourceAttributesFilter= */ null); + new Otel2PrometheusConverter( + true, /* allowedResourceAttributesFilter= */ null, /* utf8SupportEnabled */ true); @ParameterizedTest @MethodSource("metricMetadataArgs") void metricMetadata( MetricData metricData, String expectedType, String expectedHelp, String expectedMetricName) throws IOException { + assertMetricData( + metricData, + expectedType, + expectedHelp, + expectedMetricName, + new Otel2PrometheusConverter( + true, /* allowedResourceAttributesFilter= */ null, /* utf8SupportEnabled */ false), + EscapingScheme.UNDERSCORE_ESCAPING, + PATTERN); + } + + @Test + void metricMetadataUtf8() throws IOException { + // all UTF-8 chars are accepted as is + // repeated "_" are collapsed, but 2 λ are not collapsed to a single "_" + // In a real application, the escaping scheme is passed using the "escaping" header when + // scraping the metrics + + MetricData metricData = createSampleMetricData("λλbe__happy", "1", MetricDataType.LONG_GAUGE); + assertMetricData( + metricData, + "__be_happy_ratio gauge", + "__be_happy_ratio description", + "__be_happy_ratio", + converter, + EscapingScheme.UNDERSCORE_ESCAPING, + PATTERN); + + assertMetricData( + metricData, + "\"λλbe_happy_ratio\" gauge", + "\"λλbe_happy_ratio\" description", + "λλbe_happy_ratio", + converter, + EscapingScheme.ALLOW_UTF8, + ESCAPE_PATTERN); + } + + private static void assertMetricData( + MetricData metricData, + String expectedType, + String expectedHelp, + String expectedMetricName, + Otel2PrometheusConverter converter, + EscapingScheme escapingScheme, + Pattern pattern) + throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); MetricSnapshots snapshots = converter.convert(Collections.singletonList(metricData)); - ExpositionFormats.init().getPrometheusTextFormatWriter().write(out, snapshots); + ExpositionFormats.init().getPrometheusTextFormatWriter().write(out, snapshots, escapingScheme); String expositionFormat = new String(out.toByteArray(), StandardCharsets.UTF_8); assertThat(expositionFormat) .matchesSatisfying( - PATTERN, + pattern, matcher -> { assertThat(matcher.group("help")).isEqualTo(expectedHelp); assertThat(matcher.group("type")).isEqualTo(expectedType); @@ -138,12 +192,13 @@ private static Stream metricMetadataArgs() { "metric_name_2 summary", "metric_name_2 description", "metric_name_2_count"), - // unsupported characters are translated to "_", repeated "_" are dropped + // unsupported characters are translated to "_", repeated "_" are collapsed if + // the original name had consecutive "_" Arguments.of( - createSampleMetricData("s%%ple", "%/min", MetricDataType.SUMMARY), - "s_ple_percent_per_minute summary", - "s_ple_percent_per_minute description", - "s_ple_percent_per_minute_count"), + createSampleMetricData("s%%p__le", "%/min", MetricDataType.SUMMARY), + "s_p_le_percent_per_minute summary", + "s_p_le_percent_per_minute description", + "s_p_le_percent_per_minute_count"), // metric unit is not appended if the name already contains the unit Arguments.of( createSampleMetricData("metric_name_total", "total", MetricDataType.LONG_SUM), @@ -201,7 +256,8 @@ void resourceAttributesAddition( throws IOException { Otel2PrometheusConverter converter = - new Otel2PrometheusConverter(true, allowedResourceAttributesFilter); + new Otel2PrometheusConverter( + true, allowedResourceAttributesFilter, /* utf8SupportEnabled */ true); ByteArrayOutputStream out = new ByteArrayOutputStream(); MetricSnapshots snapshots = converter.convert(Collections.singletonList(metricData)); @@ -501,7 +557,10 @@ void validateCacheIsBounded() { }; Otel2PrometheusConverter otel2PrometheusConverter = - new Otel2PrometheusConverter(true, /* allowedResourceAttributesFilter= */ countPredicate); + new Otel2PrometheusConverter( + true, + /* allowedResourceAttributesFilter= */ countPredicate, + /* utf8SupportEnabled */ true); // Create 20 different metric data objects with 2 different resource attributes; Resource resource1 = Resource.builder().put("cluster", "cluster1").build(); diff --git a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReaderTest.java b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReaderTest.java index 20076cc9c9d..e19a2cbabb8 100644 --- a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReaderTest.java +++ b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReaderTest.java @@ -61,7 +61,9 @@ class PrometheusMetricReaderTest { void setUp() { this.testClock.setTime(Instant.ofEpochMilli((System.currentTimeMillis() / 100) * 100)); this.createdTimestamp = convertTimestamp(testClock.now()); - this.reader = new PrometheusMetricReader(true, /* allowedResourceAttributesFilter= */ null); + this.reader = + new PrometheusMetricReader( + true, /* allowedResourceAttributesFilter= */ null, /* utf8SupportEnabled */ true); this.meter = SdkMeterProvider.builder() .setClock(testClock) @@ -776,7 +778,8 @@ void exponentialHistogramBucketConversion() { int otelScale = random.nextInt(24) - 4; int prometheusScale = Math.min(otelScale, 8); PrometheusMetricReader reader = - new PrometheusMetricReader(true, /* allowedResourceAttributesFilter= */ null); + new PrometheusMetricReader( + true, /* allowedResourceAttributesFilter= */ null, /* utf8SupportEnabled */ true); Meter meter = SdkMeterProvider.builder() .registerMetricReader(reader) @@ -1029,7 +1032,8 @@ void otelScopeComplete() throws IOException { @Test void otelScopeDisabled() throws IOException { PrometheusMetricReader reader = - new PrometheusMetricReader(false, /* allowedResourceAttributesFilter= */ null); + new PrometheusMetricReader( + false, /* allowedResourceAttributesFilter= */ null, /* utf8SupportEnabled */ true); Meter meter = SdkMeterProvider.builder() .setClock(testClock) @@ -1060,7 +1064,9 @@ void otelScopeDisabled() throws IOException { void addResourceAttributesWorks() throws IOException { PrometheusMetricReader reader = new PrometheusMetricReader( - true, /* allowedResourceAttributesFilter= */ Predicates.is("cluster")); + true, + /* allowedResourceAttributesFilter= */ Predicates.is("cluster"), + /* utf8SupportEnabled */ true); Meter meter = SdkMeterProvider.builder() .setClock(testClock) From b701341812be6f8fe8027b58cd03749f7f1fbd91 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 25 Aug 2025 20:40:41 +0200 Subject: [PATCH 05/10] add a flag to avoid a breaking change --- .../incubator/fileconfig/MetricReaderFactoryTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricReaderFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricReaderFactoryTest.java index b0e546ea702..6f5dcb18d8e 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricReaderFactoryTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricReaderFactoryTest.java @@ -130,7 +130,8 @@ void create_PeriodicConfigured() { void create_PullPrometheusDefault() throws IOException { int port = randomAvailablePort(); List closeables = new ArrayList<>(); - PrometheusHttpServer expectedReader = PrometheusHttpServer.builder().setPort(port).build(); + PrometheusHttpServer expectedReader = + PrometheusHttpServer.builder().setUtf8SupportEnabled(true).setPort(port).build(); // Close the reader to avoid port conflict with the new instance created by MetricReaderFactory expectedReader.close(); @@ -166,6 +167,7 @@ void create_PullPrometheusConfigured() throws IOException { .setHost("localhost") .setPort(port) .setOtelScopeEnabled(false) + .setUtf8SupportEnabled(true) .setAllowedResourceAttributesFilter( IncludeExcludePredicate.createPatternMatching( singletonList("foo"), singletonList("bar"))) From 894b22631e862f7c32b57c48d5981469b604ec6e Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 26 Aug 2025 13:12:43 +0200 Subject: [PATCH 06/10] add a flag to avoid a breaking change --- .../exporter/prometheus/PrometheusHttpServerTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java index 0ff651133fc..9ff57b37a4a 100644 --- a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java +++ b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java @@ -414,6 +414,7 @@ void stringRepresentation() { + "host=localhost," + "port=0," + "otelScopeEnabled=true," + + "utf8SupportEnabled=false," + "allowedResourceAttributesFilter=null," + "memoryMode=REUSABLE_DATA," + "defaultAggregationSelector=DefaultAggregationSelector{COUNTER=default, UP_DOWN_COUNTER=default, HISTOGRAM=default, OBSERVABLE_COUNTER=default, OBSERVABLE_UP_DOWN_COUNTER=default, OBSERVABLE_GAUGE=default, GAUGE=default}" From 5842f02323b42c0c446caf986cb9e798dbd1c7ef Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Wed, 27 Aug 2025 17:52:09 +0200 Subject: [PATCH 07/10] fix --- .../exporter/prometheus/CollectorIntegrationTest.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/CollectorIntegrationTest.java b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/CollectorIntegrationTest.java index ac9bf83f5ef..58e1abed98d 100644 --- a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/CollectorIntegrationTest.java +++ b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/CollectorIntegrationTest.java @@ -133,25 +133,24 @@ void endToEnd() { assertThat(resourceMetrics.getResource().getAttributesList()) .containsExactlyInAnyOrder( // Resource attributes derived from the prometheus scrape config - stringKeyValue("service.name", "app"), stringKeyValue("service.instance.id", "host.testcontainers.internal:" + prometheusPort), stringKeyValue("server.address", "host.testcontainers.internal"), stringKeyValue("server.port", String.valueOf(prometheusPort)), stringKeyValue("url.scheme", "http"), // Resource attributes from the metric SDK resource translated to target_info stringKeyValue( - "service_name", + "service.name", Objects.requireNonNull(resource.getAttributes().get(stringKey("service.name")))), stringKeyValue( - "telemetry_sdk_name", + "telemetry.sdk.name", Objects.requireNonNull( resource.getAttributes().get(stringKey("telemetry.sdk.name")))), stringKeyValue( - "telemetry_sdk_language", + "telemetry.sdk.language", Objects.requireNonNull( resource.getAttributes().get(stringKey("telemetry.sdk.language")))), stringKeyValue( - "telemetry_sdk_version", + "telemetry.sdk.version", Objects.requireNonNull( resource.getAttributes().get(stringKey("telemetry.sdk.version"))))); From 9d9153e9e5aa90ab9a3b30d38da01134601d15e2 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 1 Sep 2025 17:44:27 +0200 Subject: [PATCH 08/10] add deprecated ctor --- .../exporter/prometheus/PrometheusMetricReader.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReader.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReader.java index 258c9531de4..3172edf00b3 100644 --- a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReader.java +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReader.java @@ -28,6 +28,16 @@ public class PrometheusMetricReader implements MetricReader, MultiCollector { private volatile CollectionRegistration collectionRegistration = CollectionRegistration.noop(); private final Otel2PrometheusConverter converter; + /** + * @deprecated use {@link #PrometheusMetricReader(boolean, Predicate, boolean)}. + */ + @Deprecated + public PrometheusMetricReader( + boolean otelScopeEnabled, @Nullable Predicate allowedResourceAttributesFilter) { + this.converter = + new Otel2PrometheusConverter(otelScopeEnabled, allowedResourceAttributesFilter, false); + } + // TODO: refactor to public static create or builder pattern to align with project style /** See {@link Otel2PrometheusConverter#Otel2PrometheusConverter(boolean, Predicate, boolean)}. */ public PrometheusMetricReader( From b986d2fabf5d58e4f4faaf3893ea5da8780a1951 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 1 Sep 2025 18:01:40 +0200 Subject: [PATCH 09/10] add deprecated ctor --- .../exporter/prometheus/PrometheusMetricReader.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReader.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReader.java index 3172edf00b3..de3543fb221 100644 --- a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReader.java +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReader.java @@ -35,7 +35,8 @@ public class PrometheusMetricReader implements MetricReader, MultiCollector { public PrometheusMetricReader( boolean otelScopeEnabled, @Nullable Predicate allowedResourceAttributesFilter) { this.converter = - new Otel2PrometheusConverter(otelScopeEnabled, allowedResourceAttributesFilter, false); + new Otel2PrometheusConverter( + otelScopeEnabled, allowedResourceAttributesFilter, /* utf8SupportEnabled= */ false); } // TODO: refactor to public static create or builder pattern to align with project style From 4c214e3f6b10c5beec16f621e628a50762526cec Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 1 Sep 2025 18:18:53 +0200 Subject: [PATCH 10/10] add deprecated ctor --- .../exporter/prometheus/PrometheusMetricReader.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReader.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReader.java index de3543fb221..210804f0e4d 100644 --- a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReader.java +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReader.java @@ -29,6 +29,8 @@ public class PrometheusMetricReader implements MetricReader, MultiCollector { private final Otel2PrometheusConverter converter; /** + * See {@link Otel2PrometheusConverter#Otel2PrometheusConverter(boolean, Predicate, boolean)}. + * * @deprecated use {@link #PrometheusMetricReader(boolean, Predicate, boolean)}. */ @Deprecated