diff --git a/exporters/prometheus/build.gradle.kts b/exporters/prometheus/build.gradle.kts index 299e741425e..6587f82784d 100644 --- a/exporters/prometheus/build.gradle.kts +++ b/exporters/prometheus/build.gradle.kts @@ -18,6 +18,7 @@ dependencies { implementation("io.prometheus:prometheus-metrics-exposition-formats-no-protobuf") compileOnly("com.google.auto.value:auto-value-annotations") + compileOnly("com.google.errorprone:error_prone_annotations") annotationProcessor("com.google.auto.value:auto-value") 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 1e952b36a09..5209f91e47b 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 @@ -80,6 +80,8 @@ final class Otel2PrometheusConverter { private static final long NANOS_PER_MILLISECOND = TimeUnit.MILLISECONDS.toNanos(1); static final int MAX_CACHE_SIZE = 10; + private final boolean otelScopeLabelsEnabled; + private final boolean targetInfoMetricEnabled; @Nullable private final Predicate allowedResourceAttributesFilter; /** @@ -89,12 +91,20 @@ final class Otel2PrometheusConverter { private final Map>> resourceAttributesToAllowedKeysCache; /** - * Constructor with feature flag parameter. + * Constructor with feature flag parameters. * + * @param otelScopeLabelsEnabled whether to add OpenTelemetry scope labels to exported metrics + * @param targetInfoMetricEnabled whether to export the target_info metric with resource + * attributes * @param allowedResourceAttributesFilter if not {@code null}, resource attributes with keys * matching this predicate will be added as labels on each exported metric */ - Otel2PrometheusConverter(@Nullable Predicate allowedResourceAttributesFilter) { + Otel2PrometheusConverter( + boolean otelScopeLabelsEnabled, + boolean targetInfoMetricEnabled, + @Nullable Predicate allowedResourceAttributesFilter) { + this.otelScopeLabelsEnabled = otelScopeLabelsEnabled; + this.targetInfoMetricEnabled = targetInfoMetricEnabled; this.allowedResourceAttributesFilter = allowedResourceAttributesFilter; this.resourceAttributesToAllowedKeysCache = allowedResourceAttributesFilter != null @@ -102,6 +112,19 @@ final class Otel2PrometheusConverter { : Collections.emptyMap(); } + boolean isOtelScopeLabelsEnabled() { + return otelScopeLabelsEnabled; + } + + boolean isTargetInfoMetricEnabled() { + return targetInfoMetricEnabled; + } + + @Nullable + Predicate getAllowedResourceAttributesFilter() { + return allowedResourceAttributesFilter; + } + MetricSnapshots convert(@Nullable Collection metricDataCollection) { if (metricDataCollection == null || metricDataCollection.isEmpty()) { return MetricSnapshots.of(); @@ -118,7 +141,7 @@ MetricSnapshots convert(@Nullable Collection metricDataCollection) { resource = metricData.getResource(); } } - if (resource != null) { + if (resource != null && targetInfoMetricEnabled) { putOrMerge(snapshotsByName, makeTargetInfo(resource)); } return new MetricSnapshots(snapshotsByName.values()); @@ -457,7 +480,7 @@ private Labels convertAttributes( requireNonNull(additionalAttributes[i]), additionalAttributes[i + 1]); } - if (scope != null) { + if (scope != null && otelScopeLabelsEnabled) { labelNameToValue.putIfAbsent(OTEL_SCOPE_NAME, scope.getName()); if (scope.getVersion() != null) { labelNameToValue.putIfAbsent(OTEL_SCOPE_VERSION, scope.getVersion()); 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 caecae015d3..f3dde0775b4 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 @@ -31,7 +31,6 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import java.util.function.Predicate; import javax.annotation.Nullable; /** @@ -42,7 +41,6 @@ public final class PrometheusHttpServer implements MetricReader { private final String host; private final int port; - @Nullable private final Predicate allowedResourceAttributesFilter; private final MemoryMode memoryMode; private final DefaultAggregationSelector defaultAggregationSelector; @@ -71,18 +69,17 @@ public static PrometheusHttpServerBuilder builder() { int port, @Nullable ExecutorService executor, PrometheusRegistry prometheusRegistry, - @Nullable Predicate allowedResourceAttributesFilter, MemoryMode memoryMode, @Nullable HttpHandler defaultHandler, DefaultAggregationSelector defaultAggregationSelector, - @Nullable Authenticator authenticator) { + @Nullable Authenticator authenticator, + PrometheusMetricReader prometheusMetricReader) { this.host = host; this.port = port; - this.allowedResourceAttributesFilter = allowedResourceAttributesFilter; this.memoryMode = memoryMode; this.defaultAggregationSelector = defaultAggregationSelector; this.builder = builder; - this.prometheusMetricReader = new PrometheusMetricReader(allowedResourceAttributesFilter); + this.prometheusMetricReader = prometheusMetricReader; this.prometheusRegistry = prometheusRegistry; prometheusRegistry.register(prometheusMetricReader); // When memory mode is REUSABLE_DATA, concurrent reads lead to data corruption. To prevent this, @@ -167,7 +164,7 @@ public String toString() { StringJoiner joiner = new StringJoiner(",", "PrometheusHttpServer{", "}"); joiner.add("host=" + host); joiner.add("port=" + port); - joiner.add("allowedResourceAttributesFilter=" + allowedResourceAttributesFilter); + joiner.add("metricReader=" + prometheusMetricReader.toString()); joiner.add("memoryMode=" + memoryMode); joiner.add( "defaultAggregationSelector=" 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 defe4091c73..1b62011e42c 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 @@ -30,7 +30,7 @@ public final class PrometheusHttpServerBuilder { private String host = DEFAULT_HOST; private int port = DEFAULT_PORT; private PrometheusRegistry prometheusRegistry = new PrometheusRegistry(); - @Nullable private Predicate allowedResourceAttributesFilter; + private PrometheusMetricReaderBuilder metricReaderBuilder = PrometheusMetricReader.builder(); @Nullable private ExecutorService executor; private MemoryMode memoryMode = DEFAULT_MEMORY_MODE; @Nullable private HttpHandler defaultHandler; @@ -44,7 +44,7 @@ public final class PrometheusHttpServerBuilder { this.host = builder.host; this.port = builder.port; this.prometheusRegistry = builder.prometheusRegistry; - this.allowedResourceAttributesFilter = builder.allowedResourceAttributesFilter; + this.metricReaderBuilder = new PrometheusMetricReaderBuilder(builder.metricReaderBuilder); this.executor = builder.executor; this.memoryMode = builder.memoryMode; this.defaultAggregationSelector = builder.defaultAggregationSelector; @@ -74,21 +74,21 @@ public PrometheusHttpServerBuilder setExecutor(ExecutorService executor) { } /** Sets the {@link PrometheusRegistry} to be used for {@link PrometheusHttpServer}. */ - @SuppressWarnings("UnusedReturnValue") public PrometheusHttpServerBuilder setPrometheusRegistry(PrometheusRegistry prometheusRegistry) { requireNonNull(prometheusRegistry, "prometheusRegistry"); this.prometheusRegistry = prometheusRegistry; return this; } - /** - * Set if the {@code otel_scope_*} attributes are generated. Default is {@code true}. - * - * @deprecated {@code otel_scope_*} attributes are always generated. - */ - @SuppressWarnings("UnusedReturnValue") - @Deprecated - public PrometheusHttpServerBuilder setOtelScopeEnabled(boolean otelScopeEnabled) { + /** Set if the {@code otel_scope_*} attributes are generated. Default is {@code true}. */ + public PrometheusHttpServerBuilder setOtelScopeLabelsEnabled(boolean otelScopeLabelsEnabled) { + metricReaderBuilder.setOtelScopeLabelsEnabled(otelScopeLabelsEnabled); + return this; + } + + /** Set if the {@code otel_target_info} metric is generated. Default is {@code true}. */ + public PrometheusHttpServerBuilder setTargetInfoMetricEnabled(boolean targetInfoMetricEnabled) { + metricReaderBuilder.setTargetInfoMetricEnabled(targetInfoMetricEnabled); return this; } @@ -104,7 +104,8 @@ public PrometheusHttpServerBuilder setOtelScopeEnabled(boolean otelScopeEnabled) */ public PrometheusHttpServerBuilder setAllowedResourceAttributesFilter( Predicate resourceAttributesFilter) { - this.allowedResourceAttributesFilter = requireNonNull(resourceAttributesFilter); + requireNonNull(resourceAttributesFilter, "resourceAttributesFilter"); + metricReaderBuilder.setAllowedResourceAttributesFilter(resourceAttributesFilter); return this; } @@ -178,10 +179,10 @@ public PrometheusHttpServer build() { port, executor, prometheusRegistry, - allowedResourceAttributesFilter, memoryMode, defaultHandler, defaultAggregationSelector, - authenticator); + authenticator, + metricReaderBuilder.build()); } } 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 ad607390023..821a7d37cb6 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 @@ -12,6 +12,7 @@ import io.opentelemetry.sdk.metrics.export.MetricReader; import io.prometheus.metrics.model.registry.MultiCollector; import io.prometheus.metrics.model.snapshots.MetricSnapshots; +import java.util.StringJoiner; import java.util.function.Predicate; import javax.annotation.Nullable; @@ -28,21 +29,54 @@ public class PrometheusMetricReader implements MetricReader, MultiCollector { private volatile CollectionRegistration collectionRegistration = CollectionRegistration.noop(); private final Otel2PrometheusConverter converter; + /** Returns a new {@link PrometheusMetricReader} with default configuration. */ + public static PrometheusMetricReader create() { + return builder().build(); + } + + /** Returns a new {@link PrometheusMetricReaderBuilder}. */ + public static PrometheusMetricReaderBuilder builder() { + return new PrometheusMetricReaderBuilder(); + } + /** - * Deprecated. Use {@link #PrometheusMetricReader(Predicate)}. + * Deprecated. Use {@link #builder()}. * - * @deprecated use {@link #PrometheusMetricReader(Predicate)}. + * @deprecated use {@link #builder()}. */ @Deprecated @SuppressWarnings({"unused", "InconsistentOverloads"}) public PrometheusMetricReader( boolean otelScopeEnabled, @Nullable Predicate allowedResourceAttributesFilter) { - this.converter = new Otel2PrometheusConverter(allowedResourceAttributesFilter); + // otelScopeEnabled parameter was used to control the scope info metric, not scope labels. + this( + allowedResourceAttributesFilter, + /* otelScopeLabelsEnabled= */ true, + /* targetInfoMetricEnabled= */ true); } - // TODO: refactor to public static create or builder pattern to align with project style + /** + * Deprecated. Use {@link #builder()}. + * + * @deprecated use {@link #builder()}. + */ + @Deprecated public PrometheusMetricReader(@Nullable Predicate allowedResourceAttributesFilter) { - this.converter = new Otel2PrometheusConverter(allowedResourceAttributesFilter); + this( + allowedResourceAttributesFilter, + /* otelScopeLabelsEnabled= */ true, + /* targetInfoMetricEnabled= */ true); + } + + // Package-private constructor used by builder + @SuppressWarnings("InconsistentOverloads") + PrometheusMetricReader( + @Nullable Predicate allowedResourceAttributesFilter, + boolean otelScopeLabelsEnabled, + boolean targetInfoMetricEnabled) { + this.converter = + new Otel2PrometheusConverter( + otelScopeLabelsEnabled, targetInfoMetricEnabled, allowedResourceAttributesFilter); } @Override @@ -69,4 +103,13 @@ public CompletableResultCode shutdown() { public MetricSnapshots collect() { return converter.convert(collectionRegistration.collectAllMetrics()); } + + @Override + public String toString() { + StringJoiner joiner = new StringJoiner(",", "PrometheusMetricReader{", "}"); + joiner.add("otelScopeLabelsEnabled=" + converter.isOtelScopeLabelsEnabled()); + joiner.add("targetInfoMetricEnabled=" + converter.isTargetInfoMetricEnabled()); + joiner.add("allowedResourceAttributesFilter=" + converter.getAllowedResourceAttributesFilter()); + return joiner.toString(); + } } diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReaderBuilder.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReaderBuilder.java new file mode 100644 index 00000000000..2d6101df416 --- /dev/null +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReaderBuilder.java @@ -0,0 +1,68 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.prometheus; + +import java.util.function.Predicate; +import javax.annotation.Nullable; + +/** Builder for {@link PrometheusMetricReader}. */ +public final class PrometheusMetricReaderBuilder { + + private boolean otelScopeLabelsEnabled = true; + private boolean targetInfoMetricEnabled = true; + @Nullable private Predicate allowedResourceAttributesFilter; + + PrometheusMetricReaderBuilder() {} + + PrometheusMetricReaderBuilder(PrometheusMetricReaderBuilder metricReaderBuilder) { + this.otelScopeLabelsEnabled = metricReaderBuilder.otelScopeLabelsEnabled; + this.targetInfoMetricEnabled = metricReaderBuilder.targetInfoMetricEnabled; + this.allowedResourceAttributesFilter = metricReaderBuilder.allowedResourceAttributesFilter; + } + + /** + * Sets whether to add OpenTelemetry scope labels (otel_scope_name, otel_scope_version, etc.) to + * exported metrics. Default is {@code true}. + * + * @param otelScopeLabelsEnabled whether to add scope labels + * @return this builder + */ + public PrometheusMetricReaderBuilder setOtelScopeLabelsEnabled(boolean otelScopeLabelsEnabled) { + this.otelScopeLabelsEnabled = otelScopeLabelsEnabled; + return this; + } + + /** + * Sets whether to export the target_info metric with resource attributes. Default is {@code + * true}. + * + * @param targetInfoMetricEnabled whether to export target_info metric + * @return this builder + */ + public PrometheusMetricReaderBuilder setTargetInfoMetricEnabled(boolean targetInfoMetricEnabled) { + this.targetInfoMetricEnabled = targetInfoMetricEnabled; + return this; + } + + /** + * Sets a filter to control which resource attributes are added as labels on each exported metric. + * If {@code null}, no resource attributes will be added as labels. Default is {@code null}. + * + * @param allowedResourceAttributesFilter predicate to filter resource attributes, or {@code null} + * @return this builder + */ + public PrometheusMetricReaderBuilder setAllowedResourceAttributesFilter( + @Nullable Predicate allowedResourceAttributesFilter) { + this.allowedResourceAttributesFilter = allowedResourceAttributesFilter; + return this; + } + + /** Builds a new {@link PrometheusMetricReader}. */ + public PrometheusMetricReader build() { + return new PrometheusMetricReader( + allowedResourceAttributesFilter, otelScopeLabelsEnabled, targetInfoMetricEnabled); + } +} 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 9968c279536..a74c5d873ac 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 @@ -45,6 +45,15 @@ public MetricReader create(DeclarativeConfigProperties config) { prometheusBuilder.setHost(host); } + Boolean withoutTargetInfo = config.getBoolean("without_target_info"); + if (withoutTargetInfo != null) { + prometheusBuilder.setTargetInfoMetricEnabled(!withoutTargetInfo); + } + Boolean withoutScopeInfo = config.getBoolean("without_scope_info"); + if (withoutScopeInfo != null) { + prometheusBuilder.setOtelScopeLabelsEnabled(!withoutScopeInfo); + } + DeclarativeConfigProperties withResourceConstantLabels = config.getStructured("with_resource_constant_labels"); if (withResourceConstantLabels != null) { 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 4660d8d1ee8..77f215504cb 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 @@ -71,7 +71,10 @@ class Otel2PrometheusConverterTest { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private final Otel2PrometheusConverter converter = - new Otel2PrometheusConverter(/* allowedResourceAttributesFilter= */ null); + new Otel2PrometheusConverter( + /* otelScopeLabelsEnabled= */ true, + /* targetInfoMetricEnabled= */ true, + /* allowedResourceAttributesFilter= */ null); @ParameterizedTest @MethodSource("metricMetadataArgs") @@ -201,7 +204,10 @@ void resourceAttributesAddition( throws IOException { Otel2PrometheusConverter converter = - new Otel2PrometheusConverter(allowedResourceAttributesFilter); + new Otel2PrometheusConverter( + /* otelScopeLabelsEnabled= */ true, + /* targetInfoMetricEnabled= */ true, + allowedResourceAttributesFilter); ByteArrayOutputStream out = new ByteArrayOutputStream(); MetricSnapshots snapshots = converter.convert(Collections.singletonList(metricData)); @@ -501,7 +507,10 @@ void validateCacheIsBounded() { }; Otel2PrometheusConverter otel2PrometheusConverter = - new Otel2PrometheusConverter(/* allowedResourceAttributesFilter= */ countPredicate); + new Otel2PrometheusConverter( + /* otelScopeLabelsEnabled= */ true, + /* targetInfoMetricEnabled= */ true, + /* allowedResourceAttributesFilter= */ countPredicate); // 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/PrometheusHttpServerTest.java b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java index fc11be75c6d..16c39efc31b 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 @@ -432,7 +432,7 @@ void stringRepresentation() { "PrometheusHttpServer{" + "host=localhost," + "port=0," - + "allowedResourceAttributesFilter=null," + + "metricReader=PrometheusMetricReader{otelScopeLabelsEnabled=true,targetInfoMetricEnabled=true,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}" + "}"); @@ -572,10 +572,14 @@ public Result authenticate(HttpExchange exchange) { .isInstanceOf(PrometheusHttpServerBuilder.class) .hasFieldOrPropertyWithValue("host", "localhost") .hasFieldOrPropertyWithValue("port", 1234) - .hasFieldOrPropertyWithValue("allowedResourceAttributesFilter", resourceAttributesFilter) .hasFieldOrPropertyWithValue("executor", executor) .hasFieldOrPropertyWithValue("prometheusRegistry", prometheusRegistry) - .hasFieldOrPropertyWithValue("authenticator", authenticator); + .hasFieldOrPropertyWithValue("authenticator", authenticator) + .extracting("metricReaderBuilder") + .usingRecursiveComparison() + .isEqualTo( + PrometheusMetricReader.builder() + .setAllowedResourceAttributesFilter(resourceAttributesFilter)); } /** 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 12bfad8d7bc..ad4dd1398a5 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 @@ -23,6 +23,7 @@ import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.View; import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.resources.ResourceBuilder; import io.opentelemetry.sdk.testing.time.TestClock; import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.prometheus.metrics.expositionformats.OpenMetricsTextFormatWriter; @@ -56,12 +57,11 @@ class PrometheusMetricReaderTest { private Meter meter; private Tracer tracer; - @SuppressWarnings("resource") @BeforeEach void setUp() { this.testClock.setTime(Instant.ofEpochMilli((System.currentTimeMillis() / 100) * 100)); this.createdTimestamp = convertTimestamp(testClock.now()); - this.reader = new PrometheusMetricReader(/* allowedResourceAttributesFilter= */ null); + this.reader = PrometheusMetricReader.create(); this.meter = SdkMeterProvider.builder() .setClock(testClock) @@ -775,8 +775,7 @@ void exponentialHistogramBucketConversion() { for (int i = 0; i < 100_000; i++) { int otelScale = random.nextInt(24) - 4; int prometheusScale = Math.min(otelScale, 8); - PrometheusMetricReader reader = - new PrometheusMetricReader(/* allowedResourceAttributesFilter= */ null); + PrometheusMetricReader reader = PrometheusMetricReader.create(); Meter meter = SdkMeterProvider.builder() .registerMetricReader(reader) @@ -998,28 +997,17 @@ void createdTimestamp() throws IOException { } @Test - void otelScopeComplete() throws IOException { - // There is currently no API for adding scope attributes. - // However, we can at least test the otel_scope_version attribute. - Meter meter = - SdkMeterProvider.builder() - .setClock(testClock) - .registerMetricReader(this.reader) - .setResource( - Resource.getDefault().toBuilder().put("telemetry.sdk.version", "1.x.x").build()) - .build() - .meterBuilder("test-scope") - .setInstrumentationVersion("a.b.c") - .build(); - LongCounter counter = meter.counterBuilder("test.count").build(); - counter.add(1); + void withoutScopeLabels() throws IOException { + PrometheusMetricReader reader = + PrometheusMetricReader.builder().setOtelScopeLabelsEnabled(false).build(); + addCounter(reader, Resource.getDefault().toBuilder()); String expected = "" + "# TYPE target info\n" + "target_info{service_name=\"unknown_service:java\",telemetry_sdk_language=\"java\",telemetry_sdk_name=\"opentelemetry\",telemetry_sdk_version=\"1.x.x\"} 1\n" + "# TYPE test_count counter\n" - + "test_count_total{otel_scope_name=\"test-scope\",otel_scope_version=\"a.b.c\"} 1.0\n" - + "test_count_created{otel_scope_name=\"test-scope\",otel_scope_version=\"a.b.c\"} " + + "test_count_total 1.0\n" + + "test_count_created " + createdTimestamp + "\n" + "# EOF\n"; @@ -1027,25 +1015,12 @@ void otelScopeComplete() throws IOException { } @Test - void otelScopeLabelsOnly() throws IOException { + void withoutTargetInfoMetric() throws IOException { PrometheusMetricReader reader = - new PrometheusMetricReader(/* allowedResourceAttributesFilter= */ null); - Meter meter = - SdkMeterProvider.builder() - .setClock(testClock) - .registerMetricReader(reader) - .setResource( - Resource.getDefault().toBuilder().put("telemetry.sdk.version", "1.x.x").build()) - .build() - .meterBuilder("test-scope") - .setInstrumentationVersion("a.b.c") - .build(); - LongCounter counter = meter.counterBuilder("test.count").build(); - counter.add(1); + PrometheusMetricReader.builder().setTargetInfoMetricEnabled(false).build(); + addCounter(reader, Resource.getDefault().toBuilder()); String expected = "" - + "# TYPE target info\n" - + "target_info{service_name=\"unknown_service:java\",telemetry_sdk_language=\"java\",telemetry_sdk_name=\"opentelemetry\",telemetry_sdk_version=\"1.x.x\"} 1\n" + "# TYPE test_count counter\n" + "test_count_total{otel_scope_name=\"test-scope\",otel_scope_version=\"a.b.c\"} 1.0\n" + "test_count_created{otel_scope_name=\"test-scope\",otel_scope_version=\"a.b.c\"} " @@ -1055,26 +1030,27 @@ void otelScopeLabelsOnly() throws IOException { assertThat(toOpenMetrics(reader.collect())).isEqualTo(expected); } - @SuppressWarnings("resource") - @Test - void addResourceAttributesWorks() throws IOException { - PrometheusMetricReader reader = - new PrometheusMetricReader(/* allowedResourceAttributesFilter= */ Predicates.is("cluster")); + private void addCounter(PrometheusMetricReader reader, ResourceBuilder builder) { Meter meter = SdkMeterProvider.builder() .setClock(testClock) .registerMetricReader(reader) - .setResource( - Resource.getDefault().toBuilder() - .put("cluster", "my.cluster") - .put("telemetry.sdk.version", "1.x.x") - .build()) + .setResource(builder.put("telemetry.sdk.version", "1.x.x").build()) .build() .meterBuilder("test-scope") .setInstrumentationVersion("a.b.c") .build(); LongCounter counter = meter.counterBuilder("test.count").build(); counter.add(1); + } + + @Test + void addResourceAttributesWorks() throws IOException { + PrometheusMetricReader reader = + PrometheusMetricReader.builder() + .setAllowedResourceAttributesFilter(Predicates.is("cluster")) + .build(); + addCounter(reader, Resource.getDefault().toBuilder().put("cluster", "my.cluster")); String expected = "" + "# TYPE target info\n" @@ -1093,7 +1069,15 @@ void addResourceAttributesWorks() throws IOException { @SuppressWarnings("deprecation") // test deprecated constructor @Test void deprecatedConstructor() { - assertThat(new PrometheusMetricReader(false, null)) + // The 2-arg deprecated constructor should behave the same as the 1-arg constructor + // when otelScopeLabelsEnabled=true (which is also the default for the builder) + assertThat(new PrometheusMetricReader(/* otelScopeEnabled= */ false, null)) + .usingRecursiveComparison() + .isEqualTo(new PrometheusMetricReader(null)); + // The 3-arg constructor should behave the same as the 2-arg deprecated constructor + assertThat( + new PrometheusMetricReader( + null, /* otelScopeLabelsEnabled= */ true, /* targetInfoMetricEnabled */ true)) .usingRecursiveComparison() .isEqualTo(new PrometheusMetricReader(null)); } 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 bc63ca7bdcb..1cc7fe94c81 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 @@ -189,7 +189,8 @@ void create_PullPrometheusConfigured() throws IOException { new IncludeExcludeModel() .withIncluded(singletonList("foo")) .withExcluded(singletonList("bar"))) - .withWithoutScopeInfo(true) + .withWithoutScopeInfo(false) + .withWithoutTargetInfo(false) .withTranslationStrategy( ExperimentalPrometheusMetricExporterModel .ExperimentalPrometheusTranslationStrategy