Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,17 @@ public ExpositionFormatWriter findWriter(@Nullable String acceptHeader) {
return prometheusProtobufWriter;
}

// Prefer OM2 over OM1 when any OM2 feature is enabled
if (isOpenMetrics2Enabled() && openMetrics2TextFormatWriter.accepts(acceptHeader)) {
return openMetrics2TextFormatWriter;
if (openMetrics2TextFormatWriter.getOpenMetrics2Properties().getContentNegotiation()) {
String version = parseOpenMetricsVersion(acceptHeader);
if ("2.0.0".equals(version)) {
return openMetrics2TextFormatWriter;
}
// version=1.0.0 or no version: fall through to OM1
} else {
// contentNegotiation=false: OM2 handles all OpenMetrics requests
return openMetrics2TextFormatWriter;
}
}

if (openMetricsTextFormatWriter.accepts(acceptHeader)) {
Expand Down Expand Up @@ -94,4 +102,27 @@ public OpenMetricsTextFormatWriter getOpenMetricsTextFormatWriter() {
public OpenMetrics2TextFormatWriter getOpenMetrics2TextFormatWriter() {
return openMetrics2TextFormatWriter;
}

@Nullable
private static String parseOpenMetricsVersion(@Nullable String acceptHeader) {
if (acceptHeader == null) {
return null;
}
for (String mediaType : acceptHeader.split(",")) {
if (mediaType.contains("application/openmetrics-text")) {
for (String param : mediaType.split(";")) {
String[] tokens = param.split("=");
if (tokens.length == 2) {
String key = tokens[0].trim();
String value = tokens[1].trim();
if (key.equals("version")) {
return value;
}
}
}
return null;
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ void testOM2EnabledWithFeatureFlags() {
.build())
.build();
ExpositionFormats formats = ExpositionFormats.init(props);
ExpositionFormatWriter writer = formats.findWriter("application/openmetrics-text");
ExpositionFormatWriter writer =
formats.findWriter("application/openmetrics-text; version=2.0.0");
assertThat(writer).isInstanceOf(OpenMetrics2TextFormatWriter.class);
}

Expand All @@ -173,6 +174,94 @@ void testProtobufWriterTakesPrecedence() {
assertThat(writer).isInstanceOf(PrometheusProtobufWriter.class);
}

@Test
void testOM2ContentNegotiationWithVersion2() {
PrometheusProperties props =
PrometheusProperties.builder()
.openMetrics2Properties(
OpenMetrics2Properties.builder().enabled(true).contentNegotiation(true).build())
.build();
ExpositionFormats formats = ExpositionFormats.init(props);
ExpositionFormatWriter writer =
formats.findWriter("application/openmetrics-text; version=2.0.0");
assertThat(writer).isInstanceOf(OpenMetrics2TextFormatWriter.class);
}

@Test
void testOM2ContentNegotiationWithVersion1() {
PrometheusProperties props =
PrometheusProperties.builder()
.openMetrics2Properties(
OpenMetrics2Properties.builder().enabled(true).contentNegotiation(true).build())
.build();
ExpositionFormats formats = ExpositionFormats.init(props);
ExpositionFormatWriter writer =
formats.findWriter("application/openmetrics-text; version=1.0.0");
assertThat(writer).isInstanceOf(OpenMetricsTextFormatWriter.class);
}

@Test
void testOM2ContentNegotiationWithNoVersion() {
// When contentNegotiation=true and Accept header has no version, use OM1 writer
PrometheusProperties props =
PrometheusProperties.builder()
.openMetrics2Properties(
OpenMetrics2Properties.builder().enabled(true).contentNegotiation(true).build())
.build();
ExpositionFormats formats = ExpositionFormats.init(props);
ExpositionFormatWriter writer = formats.findWriter("application/openmetrics-text");
assertThat(writer).isInstanceOf(OpenMetricsTextFormatWriter.class);
}

@Test
void testOM2ContentNegotiationDisabled() {
PrometheusProperties props =
PrometheusProperties.builder()
.openMetrics2Properties(
OpenMetrics2Properties.builder().enabled(true).contentNegotiation(false).build())
.build();
ExpositionFormats formats = ExpositionFormats.init(props);
ExpositionFormatWriter writer1 = formats.findWriter("application/openmetrics-text");
ExpositionFormatWriter writer2 =
formats.findWriter("application/openmetrics-text; version=1.0.0");
ExpositionFormatWriter writer3 =
formats.findWriter("application/openmetrics-text; version=2.0.0");
assertThat(writer1).isInstanceOf(OpenMetrics2TextFormatWriter.class);
assertThat(writer2).isInstanceOf(OpenMetrics2TextFormatWriter.class);
assertThat(writer3).isInstanceOf(OpenMetrics2TextFormatWriter.class);
}

@Test
void testOM2ContentNegotiationMultiTypeAcceptHeaderWithoutOMVersion() {
// When the Accept header has multiple media types and only text/plain has a version,
// the openmetrics type should be treated as having no version (use OM1).
PrometheusProperties props =
PrometheusProperties.builder()
.openMetrics2Properties(
OpenMetrics2Properties.builder().enabled(true).contentNegotiation(true).build())
.build();
ExpositionFormats formats = ExpositionFormats.init(props);
ExpositionFormatWriter writer =
formats.findWriter("application/openmetrics-text, text/plain; version=0.0.4");
assertThat(writer).isInstanceOf(OpenMetricsTextFormatWriter.class);
}

@Test
void testOM2ContentNegotiationMultiTypeAcceptHeaderWithOMVersion() {
// When the Accept header has multiple media types and openmetrics has version=2.0.0,
// the OM2 writer should be selected.
PrometheusProperties props =
PrometheusProperties.builder()
.openMetrics2Properties(
OpenMetrics2Properties.builder().enabled(true).contentNegotiation(true).build())
.build();
ExpositionFormats formats = ExpositionFormats.init(props);
ExpositionFormatWriter writer =
formats.findWriter(
"application/openmetrics-text; version=2.0.0, text/plain; version=0.0.4");
assertThat(writer).isInstanceOf(OpenMetrics2TextFormatWriter.class);
}

@Test
void testCounterComplete() throws IOException {
String openMetricsText =
Expand Down Expand Up @@ -1977,14 +2066,14 @@ void testClassicGaugeHistogramCountAndSum() throws IOException {
void testClassicHistogramWithDots() throws IOException {
String openMetricsText =
"""
# TYPE U__my_2e_request_2e_duration_2e_seconds histogram
# UNIT U__my_2e_request_2e_duration_2e_seconds seconds
# HELP U__my_2e_request_2e_duration_2e_seconds Request duration in seconds
U__my_2e_request_2e_duration_2e_seconds_bucket{U__http_2e_path="/hello",le="+Inf"} 130 # {U__some_2e_exemplar_2e_key="some value"} 3.0 1690298864.383
U__my_2e_request_2e_duration_2e_seconds_count{U__http_2e_path="/hello"} 130
U__my_2e_request_2e_duration_2e_seconds_sum{U__http_2e_path="/hello"} 0.01
# EOF
""";
# TYPE U__my_2e_request_2e_duration_2e_seconds histogram
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like maybe some of the new linting rules didn't like what was previously in this file

# UNIT U__my_2e_request_2e_duration_2e_seconds seconds
# HELP U__my_2e_request_2e_duration_2e_seconds Request duration in seconds
U__my_2e_request_2e_duration_2e_seconds_bucket{U__http_2e_path="/hello",le="+Inf"} 130 # {U__some_2e_exemplar_2e_key="some value"} 3.0 1690298864.383
U__my_2e_request_2e_duration_2e_seconds_count{U__http_2e_path="/hello"} 130
U__my_2e_request_2e_duration_2e_seconds_sum{U__http_2e_path="/hello"} 0.01
# EOF
""";
String openMetricsTextWithExemplarsOnAllTimeSeries =
"# TYPE my_request_duration_seconds histogram\n"
+ "# UNIT my_request_duration_seconds seconds\n"
Expand Down Expand Up @@ -2398,14 +2487,14 @@ void testNativeHistogramMinimal() throws IOException {
void testNativeHistogramWithDots() throws IOException {
String openMetricsText =
"""
# TYPE U__my_2e_request_2e_duration_2e_seconds histogram
# UNIT U__my_2e_request_2e_duration_2e_seconds seconds
# HELP U__my_2e_request_2e_duration_2e_seconds Request duration in seconds
U__my_2e_request_2e_duration_2e_seconds_bucket{U__http_2e_path="/hello",le="+Inf"} 4 # {U__some_2e_exemplar_2e_key="some value"} 3.0 1690298864.383
U__my_2e_request_2e_duration_2e_seconds_count{U__http_2e_path="/hello"} 4
U__my_2e_request_2e_duration_2e_seconds_sum{U__http_2e_path="/hello"} 3.2
# EOF
""";
# TYPE U__my_2e_request_2e_duration_2e_seconds histogram
# UNIT U__my_2e_request_2e_duration_2e_seconds seconds
# HELP U__my_2e_request_2e_duration_2e_seconds Request duration in seconds
U__my_2e_request_2e_duration_2e_seconds_bucket{U__http_2e_path="/hello",le="+Inf"} 4 # {U__some_2e_exemplar_2e_key="some value"} 3.0 1690298864.383
U__my_2e_request_2e_duration_2e_seconds_count{U__http_2e_path="/hello"} 4
U__my_2e_request_2e_duration_2e_seconds_sum{U__http_2e_path="/hello"} 3.2
# EOF
""";
String openMetricsTextWithExemplarsOnAllTimeSeries =
"# TYPE my_request_duration_seconds histogram\n"
+ "# UNIT my_request_duration_seconds seconds\n"
Expand Down Expand Up @@ -2876,12 +2965,19 @@ void testLabelValueEscape() throws IOException {

@ParameterizedTest
@CsvSource({
"'application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited', 'application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited; escaping=underscores'",
"'application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited',"
+ " 'application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily;"
+ " encoding=delimited; escaping=underscores'",
"'text/plain;version=0.0.4', 'text/plain; version=0.0.4; charset=utf-8; escaping=underscores'",
"'application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited; escaping=allow-utf-8', 'application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited; escaping=allow-utf-8'",
"'application/openmetrics-text', 'application/openmetrics-text; version=1.0.0; charset=utf-8; escaping=underscores'",
"'application/openmetrics-text;version=0.0.1; escaping=underscores', 'application/openmetrics-text; version=1.0.0; charset=utf-8; escaping=underscores'",
"'text/plain;version=0.0.4; escaping=allow-utf-8', 'text/plain; version=0.0.4; charset=utf-8; escaping=allow-utf-8'"
"'application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;"
+ " escaping=allow-utf-8', 'application/vnd.google.protobuf;"
+ " proto=io.prometheus.client.MetricFamily; encoding=delimited; escaping=allow-utf-8'",
"'application/openmetrics-text', 'application/openmetrics-text; version=1.0.0; charset=utf-8;"
+ " escaping=underscores'",
"'application/openmetrics-text;version=0.0.1; escaping=underscores',"
+ " 'application/openmetrics-text; version=1.0.0; charset=utf-8; escaping=underscores'",
"'text/plain;version=0.0.4; escaping=allow-utf-8', 'text/plain; version=0.0.4; charset=utf-8;"
+ " escaping=allow-utf-8'"
})
public void testFindWriter(String acceptHeaderValue, String expectedFmt) {
ExpositionFormats expositionFormats = ExpositionFormats.init();
Expand Down Expand Up @@ -2910,9 +3006,9 @@ void testWrite() throws IOException {

String expected =
"""
# TYPE foo_metric untyped
foo_metric 1.234
""";
# TYPE foo_metric untyped
foo_metric 1.234
""";

assertThat(new String(out, UTF_8)).hasToString(expected);
}
Expand Down
Loading