Skip to content

Commit da310cc

Browse files
Prom exporter update (#7934)
Co-authored-by: Jack Berg <[email protected]>
1 parent a15659d commit da310cc

File tree

11 files changed

+225
-85
lines changed

11 files changed

+225
-85
lines changed

exporters/prometheus/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ dependencies {
1818
implementation("io.prometheus:prometheus-metrics-exposition-formats-no-protobuf")
1919

2020
compileOnly("com.google.auto.value:auto-value-annotations")
21+
compileOnly("com.google.errorprone:error_prone_annotations")
2122

2223
annotationProcessor("com.google.auto.value:auto-value")
2324

exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverter.java

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ final class Otel2PrometheusConverter {
8080
private static final long NANOS_PER_MILLISECOND = TimeUnit.MILLISECONDS.toNanos(1);
8181
static final int MAX_CACHE_SIZE = 10;
8282

83+
private final boolean otelScopeLabelsEnabled;
84+
private final boolean targetInfoMetricEnabled;
8385
@Nullable private final Predicate<String> allowedResourceAttributesFilter;
8486

8587
/**
@@ -89,19 +91,40 @@ final class Otel2PrometheusConverter {
8991
private final Map<Attributes, List<AttributeKey<?>>> resourceAttributesToAllowedKeysCache;
9092

9193
/**
92-
* Constructor with feature flag parameter.
94+
* Constructor with feature flag parameters.
9395
*
96+
* @param otelScopeLabelsEnabled whether to add OpenTelemetry scope labels to exported metrics
97+
* @param targetInfoMetricEnabled whether to export the target_info metric with resource
98+
* attributes
9499
* @param allowedResourceAttributesFilter if not {@code null}, resource attributes with keys
95100
* matching this predicate will be added as labels on each exported metric
96101
*/
97-
Otel2PrometheusConverter(@Nullable Predicate<String> allowedResourceAttributesFilter) {
102+
Otel2PrometheusConverter(
103+
boolean otelScopeLabelsEnabled,
104+
boolean targetInfoMetricEnabled,
105+
@Nullable Predicate<String> allowedResourceAttributesFilter) {
106+
this.otelScopeLabelsEnabled = otelScopeLabelsEnabled;
107+
this.targetInfoMetricEnabled = targetInfoMetricEnabled;
98108
this.allowedResourceAttributesFilter = allowedResourceAttributesFilter;
99109
this.resourceAttributesToAllowedKeysCache =
100110
allowedResourceAttributesFilter != null
101111
? new ConcurrentHashMap<>()
102112
: Collections.emptyMap();
103113
}
104114

115+
boolean isOtelScopeLabelsEnabled() {
116+
return otelScopeLabelsEnabled;
117+
}
118+
119+
boolean isTargetInfoMetricEnabled() {
120+
return targetInfoMetricEnabled;
121+
}
122+
123+
@Nullable
124+
Predicate<String> getAllowedResourceAttributesFilter() {
125+
return allowedResourceAttributesFilter;
126+
}
127+
105128
MetricSnapshots convert(@Nullable Collection<MetricData> metricDataCollection) {
106129
if (metricDataCollection == null || metricDataCollection.isEmpty()) {
107130
return MetricSnapshots.of();
@@ -118,7 +141,7 @@ MetricSnapshots convert(@Nullable Collection<MetricData> metricDataCollection) {
118141
resource = metricData.getResource();
119142
}
120143
}
121-
if (resource != null) {
144+
if (resource != null && targetInfoMetricEnabled) {
122145
putOrMerge(snapshotsByName, makeTargetInfo(resource));
123146
}
124147
return new MetricSnapshots(snapshotsByName.values());
@@ -457,7 +480,7 @@ private Labels convertAttributes(
457480
requireNonNull(additionalAttributes[i]), additionalAttributes[i + 1]);
458481
}
459482

460-
if (scope != null) {
483+
if (scope != null && otelScopeLabelsEnabled) {
461484
labelNameToValue.putIfAbsent(OTEL_SCOPE_NAME, scope.getName());
462485
if (scope.getVersion() != null) {
463486
labelNameToValue.putIfAbsent(OTEL_SCOPE_VERSION, scope.getVersion());

exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServer.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import java.util.concurrent.LinkedBlockingQueue;
3232
import java.util.concurrent.ThreadPoolExecutor;
3333
import java.util.concurrent.TimeUnit;
34-
import java.util.function.Predicate;
3534
import javax.annotation.Nullable;
3635

3736
/**
@@ -42,7 +41,6 @@ public final class PrometheusHttpServer implements MetricReader {
4241

4342
private final String host;
4443
private final int port;
45-
@Nullable private final Predicate<String> allowedResourceAttributesFilter;
4644
private final MemoryMode memoryMode;
4745
private final DefaultAggregationSelector defaultAggregationSelector;
4846

@@ -71,18 +69,17 @@ public static PrometheusHttpServerBuilder builder() {
7169
int port,
7270
@Nullable ExecutorService executor,
7371
PrometheusRegistry prometheusRegistry,
74-
@Nullable Predicate<String> allowedResourceAttributesFilter,
7572
MemoryMode memoryMode,
7673
@Nullable HttpHandler defaultHandler,
7774
DefaultAggregationSelector defaultAggregationSelector,
78-
@Nullable Authenticator authenticator) {
75+
@Nullable Authenticator authenticator,
76+
PrometheusMetricReader prometheusMetricReader) {
7977
this.host = host;
8078
this.port = port;
81-
this.allowedResourceAttributesFilter = allowedResourceAttributesFilter;
8279
this.memoryMode = memoryMode;
8380
this.defaultAggregationSelector = defaultAggregationSelector;
8481
this.builder = builder;
85-
this.prometheusMetricReader = new PrometheusMetricReader(allowedResourceAttributesFilter);
82+
this.prometheusMetricReader = prometheusMetricReader;
8683
this.prometheusRegistry = prometheusRegistry;
8784
prometheusRegistry.register(prometheusMetricReader);
8885
// When memory mode is REUSABLE_DATA, concurrent reads lead to data corruption. To prevent this,
@@ -167,7 +164,7 @@ public String toString() {
167164
StringJoiner joiner = new StringJoiner(",", "PrometheusHttpServer{", "}");
168165
joiner.add("host=" + host);
169166
joiner.add("port=" + port);
170-
joiner.add("allowedResourceAttributesFilter=" + allowedResourceAttributesFilter);
167+
joiner.add("metricReader=" + prometheusMetricReader.toString());
171168
joiner.add("memoryMode=" + memoryMode);
172169
joiner.add(
173170
"defaultAggregationSelector="

exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerBuilder.java

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public final class PrometheusHttpServerBuilder {
3030
private String host = DEFAULT_HOST;
3131
private int port = DEFAULT_PORT;
3232
private PrometheusRegistry prometheusRegistry = new PrometheusRegistry();
33-
@Nullable private Predicate<String> allowedResourceAttributesFilter;
33+
private PrometheusMetricReaderBuilder metricReaderBuilder = PrometheusMetricReader.builder();
3434
@Nullable private ExecutorService executor;
3535
private MemoryMode memoryMode = DEFAULT_MEMORY_MODE;
3636
@Nullable private HttpHandler defaultHandler;
@@ -44,7 +44,7 @@ public final class PrometheusHttpServerBuilder {
4444
this.host = builder.host;
4545
this.port = builder.port;
4646
this.prometheusRegistry = builder.prometheusRegistry;
47-
this.allowedResourceAttributesFilter = builder.allowedResourceAttributesFilter;
47+
this.metricReaderBuilder = new PrometheusMetricReaderBuilder(builder.metricReaderBuilder);
4848
this.executor = builder.executor;
4949
this.memoryMode = builder.memoryMode;
5050
this.defaultAggregationSelector = builder.defaultAggregationSelector;
@@ -74,21 +74,21 @@ public PrometheusHttpServerBuilder setExecutor(ExecutorService executor) {
7474
}
7575

7676
/** Sets the {@link PrometheusRegistry} to be used for {@link PrometheusHttpServer}. */
77-
@SuppressWarnings("UnusedReturnValue")
7877
public PrometheusHttpServerBuilder setPrometheusRegistry(PrometheusRegistry prometheusRegistry) {
7978
requireNonNull(prometheusRegistry, "prometheusRegistry");
8079
this.prometheusRegistry = prometheusRegistry;
8180
return this;
8281
}
8382

84-
/**
85-
* Set if the {@code otel_scope_*} attributes are generated. Default is {@code true}.
86-
*
87-
* @deprecated {@code otel_scope_*} attributes are always generated.
88-
*/
89-
@SuppressWarnings("UnusedReturnValue")
90-
@Deprecated
91-
public PrometheusHttpServerBuilder setOtelScopeEnabled(boolean otelScopeEnabled) {
83+
/** Set if the {@code otel_scope_*} attributes are generated. Default is {@code true}. */
84+
public PrometheusHttpServerBuilder setOtelScopeLabelsEnabled(boolean otelScopeLabelsEnabled) {
85+
metricReaderBuilder.setOtelScopeLabelsEnabled(otelScopeLabelsEnabled);
86+
return this;
87+
}
88+
89+
/** Set if the {@code otel_target_info} metric is generated. Default is {@code true}. */
90+
public PrometheusHttpServerBuilder setTargetInfoMetricEnabled(boolean targetInfoMetricEnabled) {
91+
metricReaderBuilder.setTargetInfoMetricEnabled(targetInfoMetricEnabled);
9292
return this;
9393
}
9494

@@ -104,7 +104,8 @@ public PrometheusHttpServerBuilder setOtelScopeEnabled(boolean otelScopeEnabled)
104104
*/
105105
public PrometheusHttpServerBuilder setAllowedResourceAttributesFilter(
106106
Predicate<String> resourceAttributesFilter) {
107-
this.allowedResourceAttributesFilter = requireNonNull(resourceAttributesFilter);
107+
requireNonNull(resourceAttributesFilter, "resourceAttributesFilter");
108+
metricReaderBuilder.setAllowedResourceAttributesFilter(resourceAttributesFilter);
108109
return this;
109110
}
110111

@@ -178,10 +179,10 @@ public PrometheusHttpServer build() {
178179
port,
179180
executor,
180181
prometheusRegistry,
181-
allowedResourceAttributesFilter,
182182
memoryMode,
183183
defaultHandler,
184184
defaultAggregationSelector,
185-
authenticator);
185+
authenticator,
186+
metricReaderBuilder.build());
186187
}
187188
}

exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReader.java

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import io.opentelemetry.sdk.metrics.export.MetricReader;
1313
import io.prometheus.metrics.model.registry.MultiCollector;
1414
import io.prometheus.metrics.model.snapshots.MetricSnapshots;
15+
import java.util.StringJoiner;
1516
import java.util.function.Predicate;
1617
import javax.annotation.Nullable;
1718

@@ -28,21 +29,54 @@ public class PrometheusMetricReader implements MetricReader, MultiCollector {
2829
private volatile CollectionRegistration collectionRegistration = CollectionRegistration.noop();
2930
private final Otel2PrometheusConverter converter;
3031

32+
/** Returns a new {@link PrometheusMetricReader} with default configuration. */
33+
public static PrometheusMetricReader create() {
34+
return builder().build();
35+
}
36+
37+
/** Returns a new {@link PrometheusMetricReaderBuilder}. */
38+
public static PrometheusMetricReaderBuilder builder() {
39+
return new PrometheusMetricReaderBuilder();
40+
}
41+
3142
/**
32-
* Deprecated. Use {@link #PrometheusMetricReader(Predicate)}.
43+
* Deprecated. Use {@link #builder()}.
3344
*
34-
* @deprecated use {@link #PrometheusMetricReader(Predicate)}.
45+
* @deprecated use {@link #builder()}.
3546
*/
3647
@Deprecated
3748
@SuppressWarnings({"unused", "InconsistentOverloads"})
3849
public PrometheusMetricReader(
3950
boolean otelScopeEnabled, @Nullable Predicate<String> allowedResourceAttributesFilter) {
40-
this.converter = new Otel2PrometheusConverter(allowedResourceAttributesFilter);
51+
// otelScopeEnabled parameter was used to control the scope info metric, not scope labels.
52+
this(
53+
allowedResourceAttributesFilter,
54+
/* otelScopeLabelsEnabled= */ true,
55+
/* targetInfoMetricEnabled= */ true);
4156
}
4257

43-
// TODO: refactor to public static create or builder pattern to align with project style
58+
/**
59+
* Deprecated. Use {@link #builder()}.
60+
*
61+
* @deprecated use {@link #builder()}.
62+
*/
63+
@Deprecated
4464
public PrometheusMetricReader(@Nullable Predicate<String> allowedResourceAttributesFilter) {
45-
this.converter = new Otel2PrometheusConverter(allowedResourceAttributesFilter);
65+
this(
66+
allowedResourceAttributesFilter,
67+
/* otelScopeLabelsEnabled= */ true,
68+
/* targetInfoMetricEnabled= */ true);
69+
}
70+
71+
// Package-private constructor used by builder
72+
@SuppressWarnings("InconsistentOverloads")
73+
PrometheusMetricReader(
74+
@Nullable Predicate<String> allowedResourceAttributesFilter,
75+
boolean otelScopeLabelsEnabled,
76+
boolean targetInfoMetricEnabled) {
77+
this.converter =
78+
new Otel2PrometheusConverter(
79+
otelScopeLabelsEnabled, targetInfoMetricEnabled, allowedResourceAttributesFilter);
4680
}
4781

4882
@Override
@@ -69,4 +103,13 @@ public CompletableResultCode shutdown() {
69103
public MetricSnapshots collect() {
70104
return converter.convert(collectionRegistration.collectAllMetrics());
71105
}
106+
107+
@Override
108+
public String toString() {
109+
StringJoiner joiner = new StringJoiner(",", "PrometheusMetricReader{", "}");
110+
joiner.add("otelScopeLabelsEnabled=" + converter.isOtelScopeLabelsEnabled());
111+
joiner.add("targetInfoMetricEnabled=" + converter.isTargetInfoMetricEnabled());
112+
joiner.add("allowedResourceAttributesFilter=" + converter.getAllowedResourceAttributesFilter());
113+
return joiner.toString();
114+
}
72115
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.exporter.prometheus;
7+
8+
import java.util.function.Predicate;
9+
import javax.annotation.Nullable;
10+
11+
/** Builder for {@link PrometheusMetricReader}. */
12+
public final class PrometheusMetricReaderBuilder {
13+
14+
private boolean otelScopeLabelsEnabled = true;
15+
private boolean targetInfoMetricEnabled = true;
16+
@Nullable private Predicate<String> allowedResourceAttributesFilter;
17+
18+
PrometheusMetricReaderBuilder() {}
19+
20+
PrometheusMetricReaderBuilder(PrometheusMetricReaderBuilder metricReaderBuilder) {
21+
this.otelScopeLabelsEnabled = metricReaderBuilder.otelScopeLabelsEnabled;
22+
this.targetInfoMetricEnabled = metricReaderBuilder.targetInfoMetricEnabled;
23+
this.allowedResourceAttributesFilter = metricReaderBuilder.allowedResourceAttributesFilter;
24+
}
25+
26+
/**
27+
* Sets whether to add OpenTelemetry scope labels (otel_scope_name, otel_scope_version, etc.) to
28+
* exported metrics. Default is {@code true}.
29+
*
30+
* @param otelScopeLabelsEnabled whether to add scope labels
31+
* @return this builder
32+
*/
33+
public PrometheusMetricReaderBuilder setOtelScopeLabelsEnabled(boolean otelScopeLabelsEnabled) {
34+
this.otelScopeLabelsEnabled = otelScopeLabelsEnabled;
35+
return this;
36+
}
37+
38+
/**
39+
* Sets whether to export the target_info metric with resource attributes. Default is {@code
40+
* true}.
41+
*
42+
* @param targetInfoMetricEnabled whether to export target_info metric
43+
* @return this builder
44+
*/
45+
public PrometheusMetricReaderBuilder setTargetInfoMetricEnabled(boolean targetInfoMetricEnabled) {
46+
this.targetInfoMetricEnabled = targetInfoMetricEnabled;
47+
return this;
48+
}
49+
50+
/**
51+
* Sets a filter to control which resource attributes are added as labels on each exported metric.
52+
* If {@code null}, no resource attributes will be added as labels. Default is {@code null}.
53+
*
54+
* @param allowedResourceAttributesFilter predicate to filter resource attributes, or {@code null}
55+
* @return this builder
56+
*/
57+
public PrometheusMetricReaderBuilder setAllowedResourceAttributesFilter(
58+
@Nullable Predicate<String> allowedResourceAttributesFilter) {
59+
this.allowedResourceAttributesFilter = allowedResourceAttributesFilter;
60+
return this;
61+
}
62+
63+
/** Builds a new {@link PrometheusMetricReader}. */
64+
public PrometheusMetricReader build() {
65+
return new PrometheusMetricReader(
66+
allowedResourceAttributesFilter, otelScopeLabelsEnabled, targetInfoMetricEnabled);
67+
}
68+
}

exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusComponentProvider.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ public MetricReader create(DeclarativeConfigProperties config) {
4545
prometheusBuilder.setHost(host);
4646
}
4747

48+
Boolean withoutTargetInfo = config.getBoolean("without_target_info");
49+
if (withoutTargetInfo != null) {
50+
prometheusBuilder.setTargetInfoMetricEnabled(!withoutTargetInfo);
51+
}
52+
Boolean withoutScopeInfo = config.getBoolean("without_scope_info");
53+
if (withoutScopeInfo != null) {
54+
prometheusBuilder.setOtelScopeLabelsEnabled(!withoutScopeInfo);
55+
}
56+
4857
DeclarativeConfigProperties withResourceConstantLabels =
4958
config.getStructured("with_resource_constant_labels");
5059
if (withResourceConstantLabels != null) {

exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverterTest.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ class Otel2PrometheusConverterTest {
7171
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
7272

7373
private final Otel2PrometheusConverter converter =
74-
new Otel2PrometheusConverter(/* allowedResourceAttributesFilter= */ null);
74+
new Otel2PrometheusConverter(
75+
/* otelScopeLabelsEnabled= */ true,
76+
/* targetInfoMetricEnabled= */ true,
77+
/* allowedResourceAttributesFilter= */ null);
7578

7679
@ParameterizedTest
7780
@MethodSource("metricMetadataArgs")
@@ -201,7 +204,10 @@ void resourceAttributesAddition(
201204
throws IOException {
202205

203206
Otel2PrometheusConverter converter =
204-
new Otel2PrometheusConverter(allowedResourceAttributesFilter);
207+
new Otel2PrometheusConverter(
208+
/* otelScopeLabelsEnabled= */ true,
209+
/* targetInfoMetricEnabled= */ true,
210+
allowedResourceAttributesFilter);
205211

206212
ByteArrayOutputStream out = new ByteArrayOutputStream();
207213
MetricSnapshots snapshots = converter.convert(Collections.singletonList(metricData));
@@ -501,7 +507,10 @@ void validateCacheIsBounded() {
501507
};
502508

503509
Otel2PrometheusConverter otel2PrometheusConverter =
504-
new Otel2PrometheusConverter(/* allowedResourceAttributesFilter= */ countPredicate);
510+
new Otel2PrometheusConverter(
511+
/* otelScopeLabelsEnabled= */ true,
512+
/* targetInfoMetricEnabled= */ true,
513+
/* allowedResourceAttributesFilter= */ countPredicate);
505514

506515
// Create 20 different metric data objects with 2 different resource attributes;
507516
Resource resource1 = Resource.builder().put("cluster", "cluster1").build();

0 commit comments

Comments
 (0)