From 2ecd09e69168ba7b8fa47f7eb3285876339b2b5e Mon Sep 17 00:00:00 2001 From: subhramit Date: Sun, 26 Jul 2026 02:41:42 +0530 Subject: [PATCH 1/4] feat: support metric name filtering in OpenTelemetry exporter Signed-off-by: subhramit --- .../opentelemetry/OtelAutoConfig.java | 6 +- .../PrometheusMetricProducer.java | 62 ++++++++++++++++--- .../exporter/opentelemetry/ExportTest.java | 41 ++++++++++++ 3 files changed, 99 insertions(+), 10 deletions(-) diff --git a/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/OtelAutoConfig.java b/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/OtelAutoConfig.java index 2ea96e3c3..648917af7 100644 --- a/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/OtelAutoConfig.java +++ b/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/OtelAutoConfig.java @@ -41,7 +41,11 @@ static MetricReader createReader( boolean preserveNames = resolvePreserveNames(builder, config); reader.register( new PrometheusMetricProducer( - registry, instrumentationScopeInfo, getResourceField(sdk), preserveNames)); + registry, + instrumentationScopeInfo, + getResourceField(sdk), + preserveNames, + config.getExporterFilterProperties())); return reader; } diff --git a/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/PrometheusMetricProducer.java b/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/PrometheusMetricProducer.java index 886cdd85c..1c22e4944 100644 --- a/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/PrometheusMetricProducer.java +++ b/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/PrometheusMetricProducer.java @@ -7,7 +7,9 @@ import io.opentelemetry.sdk.metrics.export.CollectionRegistration; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.resources.ResourceBuilder; +import io.prometheus.metrics.config.ExporterFilterProperties; import io.prometheus.metrics.exporter.opentelemetry.otelmodel.MetricDataFactory; +import io.prometheus.metrics.model.registry.MetricNameFilter; import io.prometheus.metrics.model.registry.PrometheusRegistry; import io.prometheus.metrics.model.snapshots.CounterSnapshot; import io.prometheus.metrics.model.snapshots.GaugeSnapshot; @@ -22,6 +24,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.function.Predicate; import javax.annotation.Nullable; class PrometheusMetricProducer implements CollectionRegistration { @@ -30,29 +33,70 @@ class PrometheusMetricProducer implements CollectionRegistration { private final Resource resource; private final InstrumentationScopeInfo instrumentationScopeInfo; private final boolean preserveNames; + @Nullable private final Predicate nameFilter; + /** + * Creates a producer with no metric name filtering, i.e. all metrics in {@code registry} are + * exported. + */ public PrometheusMetricProducer( PrometheusRegistry registry, InstrumentationScopeInfo instrumentationScopeInfo, Resource resource, boolean preserveNames) { + this( + registry, + instrumentationScopeInfo, + resource, + preserveNames, + ExporterFilterProperties.builder().build()); + } + + public PrometheusMetricProducer( + PrometheusRegistry registry, + InstrumentationScopeInfo instrumentationScopeInfo, + Resource resource, + boolean preserveNames, + ExporterFilterProperties filterProperties) { this.registry = registry; this.instrumentationScopeInfo = instrumentationScopeInfo; this.resource = resource; this.preserveNames = preserveNames; + this.nameFilter = makeNameFilter(filterProperties); + } + + /** + * Builds a name filter from {@code io.prometheus.exporter.filter.*} properties, mirroring how + * {@code PrometheusScrapeHandler} builds its filter for the Servlet/HTTPServer exporters so that + * filtering config behaves consistently across exporters. + * + *

OpenTelemetry's own Views API also supports filtering and aggregation, and may be preferable + * for OpenTelemetry-specific deployments; this filter is intended for users who want the same + * {@code io.prometheus.exporter.filter.*} config to apply regardless of which exporter they use. + * + * @return {@code null} if no filter properties are set, to avoid the overhead of testing every + * metric name against a filter that matches everything. + */ + @Nullable + private static Predicate makeNameFilter(ExporterFilterProperties props) { + if (props.getAllowedMetricNames() == null + && props.getExcludedMetricNames() == null + && props.getAllowedMetricNamePrefixes() == null + && props.getExcludedMetricNamePrefixes() == null) { + return null; + } + return MetricNameFilter.builder() + .nameMustBeEqualTo(props.getAllowedMetricNames()) + .nameMustNotBeEqualTo(props.getExcludedMetricNames()) + .nameMustStartWith(props.getAllowedMetricNamePrefixes()) + .nameMustNotStartWith(props.getExcludedMetricNamePrefixes()) + .build(); } @Override public Collection collectAllMetrics() { - // Note: Currently all metrics from the registry are exported. To add metric filtering - // similar to the Servlet exporter, one could: - // 1. Add filter properties to ExporterOpenTelemetryProperties (allowedNames, excludedNames, - // etc.) - // 2. Convert these properties to a Predicate using MetricNameFilter.builder() - // 3. Call registry.scrape(filter) instead of registry.scrape() - // OpenTelemetry also provides its own Views API for filtering and aggregation, which may be - // preferred for OpenTelemetry-specific deployments. - MetricSnapshots snapshots = registry.scrape(); + MetricSnapshots snapshots = + nameFilter != null ? registry.scrape(nameFilter) : registry.scrape(); Resource resourceWithTargetInfo = resource.merge(resourceFromTargetInfo(snapshots)); InstrumentationScopeInfo scopeFromInfo = instrumentationScopeFromOtelScopeInfo(snapshots); List result = new ArrayList<>(snapshots.size()); diff --git a/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/ExportTest.java b/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/ExportTest.java index 0332eb574..17670e189 100644 --- a/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/ExportTest.java +++ b/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/ExportTest.java @@ -12,6 +12,7 @@ import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import io.opentelemetry.sdk.testing.junit5.OpenTelemetryExtension; +import io.prometheus.metrics.config.ExporterFilterProperties; import io.prometheus.metrics.core.metrics.Counter; import io.prometheus.metrics.core.metrics.Gauge; import io.prometheus.metrics.core.metrics.Histogram; @@ -381,6 +382,46 @@ void preserveNamesWithoutUnit() { OpenTelemetryAssertions.assertThat(metrics.get(0)).hasName("events_total"); } + @Test + void metricNameFilterExcludedNames() { + InMemoryMetricReader reader = InMemoryMetricReader.create(); + PrometheusRegistry filteredRegistry = new PrometheusRegistry(); + reader.register( + new PrometheusMetricProducer( + filteredRegistry, + InstrumentationScopeInfo.create("test"), + Resource.create(Attributes.builder().put("staticRes", "value").build()), + false, + ExporterFilterProperties.builder().excludedNames("secret_total").build())); + + Counter.builder().name("secret").register(filteredRegistry).inc(); + Counter.builder().name("public").register(filteredRegistry).inc(); + + List metrics = new ArrayList<>(reader.collectAllMetrics()); + assertThat(metrics).hasSize(1); + OpenTelemetryAssertions.assertThat(metrics.get(0)).hasName("public"); + } + + @Test + void metricNameFilterAllowedPrefixes() { + InMemoryMetricReader reader = InMemoryMetricReader.create(); + PrometheusRegistry filteredRegistry = new PrometheusRegistry(); + reader.register( + new PrometheusMetricProducer( + filteredRegistry, + InstrumentationScopeInfo.create("test"), + Resource.create(Attributes.builder().put("staticRes", "value").build()), + false, + ExporterFilterProperties.builder().allowedPrefixes("http_").build())); + + Counter.builder().name("http_requests").register(filteredRegistry).inc(); + Counter.builder().name("jvm_threads").register(filteredRegistry).inc(); + + List metrics = new ArrayList<>(reader.collectAllMetrics()); + assertThat(metrics).hasSize(1); + OpenTelemetryAssertions.assertThat(metrics.getFirst()).hasName("http_requests"); + } + private MetricAssert metricAssert() { List metrics = testing.getMetrics(); assertThat(metrics).hasSize(1); From 37f821b1264ac907c4ea5f82524bf8043b445c77 Mon Sep 17 00:00:00 2001 From: subhramit Date: Mon, 27 Jul 2026 18:50:56 +0530 Subject: [PATCH 2/4] feat: Use builder, fix test Signed-off-by: subhramit --- .../opentelemetry/OtelAutoConfig.java | 10 +-- .../PrometheusMetricProducer.java | 68 ++++++++++----- .../exporter/opentelemetry/ExportTest.java | 74 +++++++++------- .../opentelemetry/OtelAutoConfigTest.java | 85 ++++++++++++++++--- 4 files changed, 164 insertions(+), 73 deletions(-) diff --git a/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/OtelAutoConfig.java b/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/OtelAutoConfig.java index 648917af7..267ff23ad 100644 --- a/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/OtelAutoConfig.java +++ b/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/OtelAutoConfig.java @@ -40,12 +40,10 @@ static MetricReader createReader( MetricReader reader = requireNonNull(readerRef.get()); boolean preserveNames = resolvePreserveNames(builder, config); reader.register( - new PrometheusMetricProducer( - registry, - instrumentationScopeInfo, - getResourceField(sdk), - preserveNames, - config.getExporterFilterProperties())); + PrometheusMetricProducer.builder( + registry, instrumentationScopeInfo, getResourceField(sdk), preserveNames) + .exporterFilterProperties(config.getExporterFilterProperties()) + .build()); return reader; } diff --git a/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/PrometheusMetricProducer.java b/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/PrometheusMetricProducer.java index 1c22e4944..2c3e399f7 100644 --- a/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/PrometheusMetricProducer.java +++ b/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/PrometheusMetricProducer.java @@ -35,34 +35,29 @@ class PrometheusMetricProducer implements CollectionRegistration { private final boolean preserveNames; @Nullable private final Predicate nameFilter; - /** - * Creates a producer with no metric name filtering, i.e. all metrics in {@code registry} are - * exported. - */ - public PrometheusMetricProducer( - PrometheusRegistry registry, - InstrumentationScopeInfo instrumentationScopeInfo, - Resource resource, - boolean preserveNames) { - this( - registry, - instrumentationScopeInfo, - resource, - preserveNames, - ExporterFilterProperties.builder().build()); - } - - public PrometheusMetricProducer( + private PrometheusMetricProducer( PrometheusRegistry registry, InstrumentationScopeInfo instrumentationScopeInfo, Resource resource, boolean preserveNames, - ExporterFilterProperties filterProperties) { + @Nullable Predicate nameFilter) { this.registry = registry; this.instrumentationScopeInfo = instrumentationScopeInfo; this.resource = resource; this.preserveNames = preserveNames; - this.nameFilter = makeNameFilter(filterProperties); + this.nameFilter = nameFilter; + } + + /** + * Creates a builder for a producer with no metric name filtering by default, i.e. all metrics in + * {@code registry} are exported unless filter properties are configured on the builder. + */ + static Builder builder( + PrometheusRegistry registry, + InstrumentationScopeInfo instrumentationScopeInfo, + Resource resource, + boolean preserveNames) { + return new Builder(registry, instrumentationScopeInfo, resource, preserveNames); } /** @@ -186,4 +181,37 @@ private void addUnlessNull(List result, @Nullable MetricData data) { result.add(data); } } + + static class Builder { + private final PrometheusRegistry registry; + private final Resource resource; + private final InstrumentationScopeInfo instrumentationScopeInfo; + private final boolean preserveNames; + private ExporterFilterProperties filterProperties = ExporterFilterProperties.builder().build(); + + private Builder( + PrometheusRegistry registry, + InstrumentationScopeInfo instrumentationScopeInfo, + Resource resource, + boolean preserveNames) { + this.registry = registry; + this.instrumentationScopeInfo = instrumentationScopeInfo; + this.resource = resource; + this.preserveNames = preserveNames; + } + + Builder exporterFilterProperties(ExporterFilterProperties filterProperties) { + this.filterProperties = filterProperties; + return this; + } + + PrometheusMetricProducer build() { + return new PrometheusMetricProducer( + registry, + instrumentationScopeInfo, + resource, + preserveNames, + makeNameFilter(filterProperties)); + } + } } diff --git a/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/ExportTest.java b/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/ExportTest.java index 17670e189..4329df4b8 100644 --- a/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/ExportTest.java +++ b/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/ExportTest.java @@ -47,11 +47,12 @@ void setUp() throws IllegalAccessException, NoSuchFieldException { MetricReader reader = (MetricReader) field.get(testing); PrometheusMetricProducer prometheusMetricProducer = - new PrometheusMetricProducer( - registry, - InstrumentationScopeInfo.create("test"), - Resource.create(Attributes.builder().put("staticRes", "value").build()), - false); + PrometheusMetricProducer.builder( + registry, + InstrumentationScopeInfo.create("test"), + Resource.create(Attributes.builder().put("staticRes", "value").build()), + false) + .build(); reader.register(prometheusMetricProducer); } @@ -333,11 +334,12 @@ void preserveNamesWithUnit() { InMemoryMetricReader reader = InMemoryMetricReader.create(); PrometheusRegistry preserveRegistry = new PrometheusRegistry(); reader.register( - new PrometheusMetricProducer( - preserveRegistry, - InstrumentationScopeInfo.create("test"), - Resource.create(Attributes.builder().put("staticRes", "value").build()), - true)); + PrometheusMetricProducer.builder( + preserveRegistry, + InstrumentationScopeInfo.create("test"), + Resource.create(Attributes.builder().put("staticRes", "value").build()), + true) + .build()); Counter.builder().name("req").unit(Unit.BYTES).register(preserveRegistry).inc(); @@ -351,11 +353,12 @@ void preserveNamesWithUnitAlreadyInName() { InMemoryMetricReader reader = InMemoryMetricReader.create(); PrometheusRegistry preserveRegistry = new PrometheusRegistry(); reader.register( - new PrometheusMetricProducer( - preserveRegistry, - InstrumentationScopeInfo.create("test"), - Resource.create(Attributes.builder().put("staticRes", "value").build()), - true)); + PrometheusMetricProducer.builder( + preserveRegistry, + InstrumentationScopeInfo.create("test"), + Resource.create(Attributes.builder().put("staticRes", "value").build()), + true) + .build()); Counter.builder().name("req_bytes").unit(Unit.BYTES).register(preserveRegistry).inc(); @@ -369,11 +372,12 @@ void preserveNamesWithoutUnit() { InMemoryMetricReader reader = InMemoryMetricReader.create(); PrometheusRegistry preserveRegistry = new PrometheusRegistry(); reader.register( - new PrometheusMetricProducer( - preserveRegistry, - InstrumentationScopeInfo.create("test"), - Resource.create(Attributes.builder().put("staticRes", "value").build()), - true)); + PrometheusMetricProducer.builder( + preserveRegistry, + InstrumentationScopeInfo.create("test"), + Resource.create(Attributes.builder().put("staticRes", "value").build()), + true) + .build()); Counter.builder().name("events_total").register(preserveRegistry).inc(); @@ -387,12 +391,14 @@ void metricNameFilterExcludedNames() { InMemoryMetricReader reader = InMemoryMetricReader.create(); PrometheusRegistry filteredRegistry = new PrometheusRegistry(); reader.register( - new PrometheusMetricProducer( - filteredRegistry, - InstrumentationScopeInfo.create("test"), - Resource.create(Attributes.builder().put("staticRes", "value").build()), - false, - ExporterFilterProperties.builder().excludedNames("secret_total").build())); + PrometheusMetricProducer.builder( + filteredRegistry, + InstrumentationScopeInfo.create("test"), + Resource.create(Attributes.builder().put("staticRes", "value").build()), + false) + .exporterFilterProperties( + ExporterFilterProperties.builder().excludedNames("secret_total").build()) + .build()); Counter.builder().name("secret").register(filteredRegistry).inc(); Counter.builder().name("public").register(filteredRegistry).inc(); @@ -407,19 +413,21 @@ void metricNameFilterAllowedPrefixes() { InMemoryMetricReader reader = InMemoryMetricReader.create(); PrometheusRegistry filteredRegistry = new PrometheusRegistry(); reader.register( - new PrometheusMetricProducer( - filteredRegistry, - InstrumentationScopeInfo.create("test"), - Resource.create(Attributes.builder().put("staticRes", "value").build()), - false, - ExporterFilterProperties.builder().allowedPrefixes("http_").build())); + PrometheusMetricProducer.builder( + filteredRegistry, + InstrumentationScopeInfo.create("test"), + Resource.create(Attributes.builder().put("staticRes", "value").build()), + false) + .exporterFilterProperties( + ExporterFilterProperties.builder().allowedPrefixes("http_").build()) + .build()); Counter.builder().name("http_requests").register(filteredRegistry).inc(); Counter.builder().name("jvm_threads").register(filteredRegistry).inc(); List metrics = new ArrayList<>(reader.collectAllMetrics()); assertThat(metrics).hasSize(1); - OpenTelemetryAssertions.assertThat(metrics.getFirst()).hasName("http_requests"); + OpenTelemetryAssertions.assertThat(metrics.get(0)).hasName("http_requests"); } private MetricAssert metricAssert() { diff --git a/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/OtelAutoConfigTest.java b/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/OtelAutoConfigTest.java index a81aec440..6bd685a1e 100644 --- a/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/OtelAutoConfigTest.java +++ b/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/OtelAutoConfigTest.java @@ -7,11 +7,21 @@ import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.export.CollectionRegistration; +import io.opentelemetry.sdk.metrics.export.MetricReader; +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; +import io.prometheus.metrics.config.ExporterFilterProperties; import io.prometheus.metrics.config.ExporterOpenTelemetryProperties; import io.prometheus.metrics.config.PrometheusProperties; import io.prometheus.metrics.config.PrometheusPropertiesLoader; +import io.prometheus.metrics.core.metrics.Counter; +import io.prometheus.metrics.model.registry.PrometheusRegistry; +import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; @@ -67,21 +77,16 @@ public static Stream testCases() { "values from builder", new TestCase() .expectedProperties( - Map.of( - "otel.exporter.otlp.protocol", - Optional.of("http/protobuf"), - "otel.exporter.otlp.endpoint", - Optional.of("http://builder:4318"), - "otel.exporter.otlp.headers", - Optional.of("h=builder-v"), - "otel.metric.export.interval", - Optional.of("2s"), - "otel.exporter.otlp.timeout", - Optional.of("3s"), - "otel.service.name", - Optional.of("builder-service"))) + ImmutableMap.>builder() + .put("otel.exporter.otlp.protocol", Optional.of("http/protobuf")) + .put("otel.exporter.otlp.endpoint", Optional.of("http://builder:4318")) + .put("otel.exporter.otlp.headers", Optional.of("h=builder-v")) + .put("otel.metric.export.interval", Optional.of("2s")) + .put("otel.exporter.otlp.timeout", Optional.of("3s")) + .put("otel.service.name", Optional.of("builder-service")) + .build()) .expectedResourceAttributes( - Map.of( + ImmutableMap.of( "key", "builder-value", "service.name", @@ -316,6 +321,58 @@ void resolvePreserveNamesFromConfig() { assertThat(OtelAutoConfig.resolvePreserveNames(builder, config)).isTrue(); } + @Test + void createReaderUsesExporterFilterProperties() throws IllegalAccessException { + PrometheusRegistry registry = new PrometheusRegistry(); + PrometheusProperties config = + PrometheusProperties.builder() + .exporterFilterProperties( + ExporterFilterProperties.builder().excludedNames("secret_total").build()) + .build(); + OpenTelemetryExporter.Builder builder = + OpenTelemetryExporter.builder(config).registry(registry); + MetricReader reader = OtelAutoConfig.createReader(builder, config, registry); + + try { + Counter.builder().name("secret").register(registry).inc(); + Counter.builder().name("public").register(registry).inc(); + + PrometheusMetricProducer producer = findPrometheusMetricProducer(reader); + List metrics = new ArrayList<>(producer.collectAllMetrics()); + assertThat(metrics).hasSize(1); + OpenTelemetryAssertions.assertThat(metrics.get(0)).hasName("public"); + } finally { + reader.shutdown(); + } + } + + private static PrometheusMetricProducer findPrometheusMetricProducer(Object object) + throws IllegalAccessException { + assertThat(object).isNotNull(); + for (Field field : object.getClass().getDeclaredFields()) { + field.setAccessible(true); + Object value = field.get(object); + if (value instanceof PrometheusMetricProducer) { + return (PrometheusMetricProducer) value; + } + if (value instanceof CollectionRegistration) { + return findPrometheusMetricProducer(value); + } + if (value instanceof Iterable) { + for (Object element : (Iterable) value) { + if (element instanceof PrometheusMetricProducer) { + return (PrometheusMetricProducer) element; + } + if (element instanceof CollectionRegistration) { + return findPrometheusMetricProducer(element); + } + } + } + } + throw new AssertionError( + "Did not find PrometheusMetricProducer in " + object.getClass().getName()); + } + private static ExporterOpenTelemetryProperties getExporterOpenTelemetryProperties( TestCase testCase) { if (testCase.propertiesBuilder == null) { From 755d68d5df5f4c29ffdd43ee8c342bbb3edafbd1 Mon Sep 17 00:00:00 2001 From: subhramit Date: Mon, 27 Jul 2026 18:54:56 +0530 Subject: [PATCH 3/4] docs: add comment to test Signed-off-by: subhramit --- .../metrics/exporter/opentelemetry/OtelAutoConfigTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/OtelAutoConfigTest.java b/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/OtelAutoConfigTest.java index 6bd685a1e..19a737122 100644 --- a/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/OtelAutoConfigTest.java +++ b/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/OtelAutoConfigTest.java @@ -346,6 +346,9 @@ void createReaderUsesExporterFilterProperties() throws IllegalAccessException { } } + // MetricReader does not expose registered CollectionRegistrations, so this test locates the + // registered PrometheusMetricProducer reflectively to verify that OtelAutoConfig wires exporter + // filter properties through to it. private static PrometheusMetricProducer findPrometheusMetricProducer(Object object) throws IllegalAccessException { assertThat(object).isNotNull(); From d23aa3368708a825fefee5156f40c1df76a6e05a Mon Sep 17 00:00:00 2001 From: subhramit Date: Mon, 27 Jul 2026 22:16:09 +0530 Subject: [PATCH 4/4] docs: add note to otlp.md Signed-off-by: subhramit --- docs/content/otel/otlp.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/content/otel/otlp.md b/docs/content/otel/otlp.md index 31aacfcbd..e2ea987dd 100644 --- a/docs/content/otel/otlp.md +++ b/docs/content/otel/otlp.md @@ -44,6 +44,9 @@ By default, the `OpenTelemetryExporter` will push metrics every 60 seconds to the [OpenTelemetryExporter.Builder][builder-javadoc], or at runtime via [`io.prometheus.exporter.opentelemetry.*`][otel-properties] properties. +The OpenTelemetry exporter also honors the shared [`io.prometheus.exporter.filter.*`][exporter-filter-properties] metric-name +filter properties. + In addition to the Prometheus Java client configuration, the exporter also recognizes standard OpenTelemetry configuration. For example, you can set the [OTEL_EXPORTER_OTLP_METRICS_ENDPOINT](https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/#otel_exporter_otlp_metrics_endpoint) @@ -62,4 +65,5 @@ OTel collector, and a Prometheus server. [builder-javadoc]: /client_java/api/io/prometheus/metrics/exporter/opentelemetry/OpenTelemetryExporter.Builder.html [opentelemetry-example]: https://github.com/prometheus/client_java/tree/main/examples/example-exporter-opentelemetry [otel-pipeline]: /client_java/images/otel-pipeline.png +[exporter-filter-properties]: {{< relref "../config/config.md#exporter-filter-properties" >}} [otel-properties]: {{< relref "../config/config.md#exporter-opentelemetry-properties" >}}