Skip to content

Commit ffa0d39

Browse files
authored
feat: Add OpenMetrics2 enabled flag (#1953)
## Summary - Add `io.prometheus.openmetrics2.enabled` as the explicit gate for OM2 writer selection. Feature flags alone no longer activate OM2. - `enableOpenMetrics2()` programmatic configurator sets `enabled=true` implicitly. - Prerequisite for #1942 — gives users a clean way to opt into OM2's name-preservation behavior without committing to other OM2 features. ## Test plan - [x] `OpenMetrics2PropertiesTest` covers `enabled` in load, builder, enableAll, defaultValues - [x] `ExpositionFormatsTest` verifies OM2 activation requires `enabled=true`, feature flags alone fall back to OM1 - [x] Full build + test suite passes Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
1 parent 01f53e9 commit ffa0d39

5 files changed

Lines changed: 47 additions & 43 deletions

File tree

prometheus-metrics-config/src/main/java/io/prometheus/metrics/config/OpenMetrics2Properties.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,39 @@
99
public class OpenMetrics2Properties {
1010

1111
private static final String PREFIX = "io.prometheus.openmetrics2";
12+
private static final String ENABLED = "enabled";
1213
private static final String CONTENT_NEGOTIATION = "content_negotiation";
1314
private static final String COMPOSITE_VALUES = "composite_values";
1415
private static final String EXEMPLAR_COMPLIANCE = "exemplar_compliance";
1516
private static final String NATIVE_HISTOGRAMS = "native_histograms";
1617

18+
@Nullable private final Boolean enabled;
1719
@Nullable private final Boolean contentNegotiation;
1820
@Nullable private final Boolean compositeValues;
1921
@Nullable private final Boolean exemplarCompliance;
2022
@Nullable private final Boolean nativeHistograms;
2123

2224
private OpenMetrics2Properties(
25+
@Nullable Boolean enabled,
2326
@Nullable Boolean contentNegotiation,
2427
@Nullable Boolean compositeValues,
2528
@Nullable Boolean exemplarCompliance,
2629
@Nullable Boolean nativeHistograms) {
30+
this.enabled = enabled;
2731
this.contentNegotiation = contentNegotiation;
2832
this.compositeValues = compositeValues;
2933
this.exemplarCompliance = exemplarCompliance;
3034
this.nativeHistograms = nativeHistograms;
3135
}
3236

37+
/**
38+
* Enable the OpenMetrics 2.0 text format writer. When {@code true}, the OM2 writer is used
39+
* instead of OM1 for OpenMetrics responses. Default is {@code false}.
40+
*/
41+
public boolean getEnabled() {
42+
return enabled != null && enabled;
43+
}
44+
3345
/** Gate OM2 features behind content negotiation. Default is {@code false}. */
3446
public boolean getContentNegotiation() {
3547
return contentNegotiation != null && contentNegotiation;
@@ -56,12 +68,13 @@ public boolean getNativeHistograms() {
5668
*/
5769
static OpenMetrics2Properties load(PropertySource propertySource)
5870
throws PrometheusPropertiesException {
71+
Boolean enabled = Util.loadBoolean(PREFIX, ENABLED, propertySource);
5972
Boolean contentNegotiation = Util.loadBoolean(PREFIX, CONTENT_NEGOTIATION, propertySource);
6073
Boolean compositeValues = Util.loadBoolean(PREFIX, COMPOSITE_VALUES, propertySource);
6174
Boolean exemplarCompliance = Util.loadBoolean(PREFIX, EXEMPLAR_COMPLIANCE, propertySource);
6275
Boolean nativeHistograms = Util.loadBoolean(PREFIX, NATIVE_HISTOGRAMS, propertySource);
6376
return new OpenMetrics2Properties(
64-
contentNegotiation, compositeValues, exemplarCompliance, nativeHistograms);
77+
enabled, contentNegotiation, compositeValues, exemplarCompliance, nativeHistograms);
6578
}
6679

6780
public static Builder builder() {
@@ -70,13 +83,20 @@ public static Builder builder() {
7083

7184
public static class Builder {
7285

86+
@Nullable private Boolean enabled;
7387
@Nullable private Boolean contentNegotiation;
7488
@Nullable private Boolean compositeValues;
7589
@Nullable private Boolean exemplarCompliance;
7690
@Nullable private Boolean nativeHistograms;
7791

7892
private Builder() {}
7993

94+
/** See {@link #getEnabled()} */
95+
public Builder enabled(boolean enabled) {
96+
this.enabled = enabled;
97+
return this;
98+
}
99+
80100
/** See {@link #getContentNegotiation()} */
81101
public Builder contentNegotiation(boolean contentNegotiation) {
82102
this.contentNegotiation = contentNegotiation;
@@ -103,6 +123,7 @@ public Builder nativeHistograms(boolean nativeHistograms) {
103123

104124
/** Enable all OpenMetrics 2.0 features */
105125
public Builder enableAll() {
126+
this.enabled = true;
106127
this.contentNegotiation = true;
107128
this.compositeValues = true;
108129
this.exemplarCompliance = true;
@@ -112,7 +133,7 @@ public Builder enableAll() {
112133

113134
public OpenMetrics2Properties build() {
114135
return new OpenMetrics2Properties(
115-
contentNegotiation, compositeValues, exemplarCompliance, nativeHistograms);
136+
enabled, contentNegotiation, compositeValues, exemplarCompliance, nativeHistograms);
116137
}
117138
}
118139
}

prometheus-metrics-config/src/main/java/io/prometheus/metrics/config/PrometheusProperties.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,8 @@ public Builder exporterOpenTelemetryProperties(
242242
}
243243

244244
public Builder enableOpenMetrics2(Consumer<OpenMetrics2Properties.Builder> configurator) {
245-
OpenMetrics2Properties.Builder openMetrics2Builder = OpenMetrics2Properties.builder();
245+
OpenMetrics2Properties.Builder openMetrics2Builder =
246+
OpenMetrics2Properties.builder().enabled(true);
246247
configurator.accept(openMetrics2Builder);
247248
this.openMetrics2Properties = openMetrics2Builder.build();
248249
return this;

prometheus-metrics-config/src/test/java/io/prometheus/metrics/config/OpenMetrics2PropertiesTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ void load() {
1515
load(
1616
new HashMap<>(
1717
Map.of(
18+
"io.prometheus.openmetrics2.enabled",
19+
"true",
1820
"io.prometheus.openmetrics2.content_negotiation",
1921
"true",
2022
"io.prometheus.openmetrics2.composite_values",
@@ -23,6 +25,7 @@ void load() {
2325
"true",
2426
"io.prometheus.openmetrics2.native_histograms",
2527
"true")));
28+
assertThat(properties.getEnabled()).isTrue();
2629
assertThat(properties.getContentNegotiation()).isTrue();
2730
assertThat(properties.getCompositeValues()).isTrue();
2831
assertThat(properties.getExemplarCompliance()).isTrue();
@@ -31,6 +34,11 @@ void load() {
3134

3235
@Test
3336
void loadInvalidValue() {
37+
assertThatExceptionOfType(PrometheusPropertiesException.class)
38+
.isThrownBy(
39+
() -> load(new HashMap<>(Map.of("io.prometheus.openmetrics2.enabled", "invalid"))))
40+
.withMessage(
41+
"io.prometheus.openmetrics2.enabled: Expecting 'true' or 'false'. Found: invalid");
3442
assertThatExceptionOfType(PrometheusPropertiesException.class)
3543
.isThrownBy(
3644
() ->
@@ -79,11 +87,13 @@ private static OpenMetrics2Properties load(Map<String, String> map) {
7987
void builder() {
8088
OpenMetrics2Properties properties =
8189
OpenMetrics2Properties.builder()
90+
.enabled(true)
8291
.contentNegotiation(true)
8392
.compositeValues(false)
8493
.exemplarCompliance(true)
8594
.nativeHistograms(false)
8695
.build();
96+
assertThat(properties.getEnabled()).isTrue();
8797
assertThat(properties.getContentNegotiation()).isTrue();
8898
assertThat(properties.getCompositeValues()).isFalse();
8999
assertThat(properties.getExemplarCompliance()).isTrue();
@@ -93,6 +103,7 @@ void builder() {
93103
@Test
94104
void builderEnableAll() {
95105
OpenMetrics2Properties properties = OpenMetrics2Properties.builder().enableAll().build();
106+
assertThat(properties.getEnabled()).isTrue();
96107
assertThat(properties.getContentNegotiation()).isTrue();
97108
assertThat(properties.getCompositeValues()).isTrue();
98109
assertThat(properties.getExemplarCompliance()).isTrue();
@@ -102,6 +113,7 @@ void builderEnableAll() {
102113
@Test
103114
void defaultValues() {
104115
OpenMetrics2Properties properties = OpenMetrics2Properties.builder().build();
116+
assertThat(properties.getEnabled()).isFalse();
105117
assertThat(properties.getContentNegotiation()).isFalse();
106118
assertThat(properties.getCompositeValues()).isFalse();
107119
assertThat(properties.getExemplarCompliance()).isFalse();

prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/ExpositionFormats.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,7 @@ public ExpositionFormatWriter findWriter(@Nullable String acceptHeader) {
7676
}
7777

7878
private boolean isOpenMetrics2Enabled() {
79-
OpenMetrics2Properties props = openMetrics2TextFormatWriter.getOpenMetrics2Properties();
80-
return props.getContentNegotiation()
81-
|| props.getCompositeValues()
82-
|| props.getExemplarCompliance()
83-
|| props.getNativeHistograms();
79+
return openMetrics2TextFormatWriter.getOpenMetrics2Properties().getEnabled();
8480
}
8581

8682
public PrometheusProtobufWriter getPrometheusProtobufWriter() {

prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/ExpositionFormatsTest.java

Lines changed: 9 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -119,77 +119,51 @@ void testOM2DisabledByDefault() {
119119
}
120120

121121
@Test
122-
void testOM2EnabledWithContentNegotiation() {
122+
void testOM2EnabledOnly() {
123123
PrometheusProperties props =
124124
PrometheusProperties.builder()
125-
.openMetrics2Properties(
126-
OpenMetrics2Properties.builder().contentNegotiation(true).build())
125+
.openMetrics2Properties(OpenMetrics2Properties.builder().enabled(true).build())
127126
.build();
128127
ExpositionFormats formats = ExpositionFormats.init(props);
129128
ExpositionFormatWriter writer = formats.findWriter("application/openmetrics-text");
130-
// When contentNegotiation is enabled, should return OM2 writer
131129
assertThat(writer).isInstanceOf(OpenMetrics2TextFormatWriter.class);
132130
}
133131

134132
@Test
135-
void testOM2EnabledWithCompositeValues() {
136-
PrometheusProperties props =
137-
PrometheusProperties.builder()
138-
.openMetrics2Properties(OpenMetrics2Properties.builder().compositeValues(true).build())
139-
.build();
140-
ExpositionFormats formats = ExpositionFormats.init(props);
141-
ExpositionFormatWriter writer = formats.findWriter("application/openmetrics-text");
142-
assertThat(writer).isInstanceOf(OpenMetrics2TextFormatWriter.class);
143-
}
144-
145-
@Test
146-
void testOM2EnabledWithExemplarCompliance() {
133+
void testOM2NotEnabledByFeatureFlagAlone() {
134+
// Feature flags without enabled=true should not activate the OM2 writer
147135
PrometheusProperties props =
148136
PrometheusProperties.builder()
149137
.openMetrics2Properties(
150-
OpenMetrics2Properties.builder().exemplarCompliance(true).build())
151-
.build();
152-
ExpositionFormats formats = ExpositionFormats.init(props);
153-
ExpositionFormatWriter writer = formats.findWriter("application/openmetrics-text");
154-
// When exemplarCompliance is enabled, should return OM2 writer
155-
assertThat(writer).isInstanceOf(OpenMetrics2TextFormatWriter.class);
156-
}
157-
158-
@Test
159-
void testOM2EnabledWithNativeHistograms() {
160-
PrometheusProperties props =
161-
PrometheusProperties.builder()
162-
.openMetrics2Properties(OpenMetrics2Properties.builder().nativeHistograms(true).build())
138+
OpenMetrics2Properties.builder().contentNegotiation(true).build())
163139
.build();
164140
ExpositionFormats formats = ExpositionFormats.init(props);
165141
ExpositionFormatWriter writer = formats.findWriter("application/openmetrics-text");
166-
// When nativeHistograms is enabled, should return OM2 writer
167-
assertThat(writer).isInstanceOf(OpenMetrics2TextFormatWriter.class);
142+
assertThat(writer).isInstanceOf(OpenMetricsTextFormatWriter.class);
168143
}
169144

170145
@Test
171-
void testOM2EnabledWithMultipleFlags() {
146+
void testOM2EnabledWithFeatureFlags() {
172147
PrometheusProperties props =
173148
PrometheusProperties.builder()
174149
.openMetrics2Properties(
175150
OpenMetrics2Properties.builder()
151+
.enabled(true)
176152
.contentNegotiation(true)
177153
.compositeValues(true)
178154
.nativeHistograms(true)
179155
.build())
180156
.build();
181157
ExpositionFormats formats = ExpositionFormats.init(props);
182158
ExpositionFormatWriter writer = formats.findWriter("application/openmetrics-text");
183-
// When multiple OM2 flags are enabled, should return OM2 writer
184159
assertThat(writer).isInstanceOf(OpenMetrics2TextFormatWriter.class);
185160
}
186161

187162
@Test
188163
void testProtobufWriterTakesPrecedence() {
189164
PrometheusProperties props =
190165
PrometheusProperties.builder()
191-
.openMetrics2Properties(
192-
OpenMetrics2Properties.builder().contentNegotiation(true).build())
166+
.openMetrics2Properties(OpenMetrics2Properties.builder().enabled(true).build())
193167
.build();
194168
ExpositionFormats formats = ExpositionFormats.init(props);
195169
ExpositionFormatWriter writer =

0 commit comments

Comments
 (0)