Fix broken content negotiation for Prometheus with OpenMetrics

Update Prometheus `TextOutputFormat` so that OpenMetrics is used in
preference to text output when an appropriate accept header is found.

If the accept header contains `*/*` or is missing then the text format
will be used.

See gh-28130
pull/28204/head
Andy Wilkinson 3 years ago
parent d8141e6a8d
commit 437a1601ef

@ -36,25 +36,30 @@ import org.springframework.util.MimeTypeUtils;
public enum TextOutputFormat implements Producible<TextOutputFormat> { public enum TextOutputFormat implements Producible<TextOutputFormat> {
/** /**
* OpenMetrics text version 1.0.0. * Prometheus text version 0.0.4.
*/ */
CONTENT_TYPE_OPENMETRICS_100(TextFormat.CONTENT_TYPE_OPENMETRICS_100) { CONTENT_TYPE_004(TextFormat.CONTENT_TYPE_004) {
@Override @Override
void write(Writer writer, Enumeration<MetricFamilySamples> samples) throws IOException { void write(Writer writer, Enumeration<MetricFamilySamples> samples) throws IOException {
TextFormat.writeOpenMetrics100(writer, samples); TextFormat.write004(writer, samples);
}
@Override
public boolean isDefault() {
return true;
} }
}, },
/** /**
* Prometheus text version 0.0.4. * OpenMetrics text version 1.0.0.
*/ */
CONTENT_TYPE_004(TextFormat.CONTENT_TYPE_004) { CONTENT_TYPE_OPENMETRICS_100(TextFormat.CONTENT_TYPE_OPENMETRICS_100) {
@Override @Override
void write(Writer writer, Enumeration<MetricFamilySamples> samples) throws IOException { void write(Writer writer, Enumeration<MetricFamilySamples> samples) throws IOException {
TextFormat.write004(writer, samples); TextFormat.writeOpenMetrics100(writer, samples);
} }
}; };

@ -55,6 +55,14 @@ class PrometheusScrapeEndpointIntegrationTests {
.contains("counter1_total").contains("counter2_total").contains("counter3_total")); .contains("counter1_total").contains("counter2_total").contains("counter3_total"));
} }
@WebEndpointTest
void scrapePrefersToProduceOpenMetrics100(WebTestClient client) {
MediaType openMetrics = MediaType.parseMediaType(TextFormat.CONTENT_TYPE_OPENMETRICS_100);
MediaType textPlain = MediaType.parseMediaType(TextFormat.CONTENT_TYPE_004);
client.get().uri("/actuator/prometheus").accept(openMetrics, textPlain).exchange().expectStatus().isOk()
.expectHeader().contentType(openMetrics);
}
@WebEndpointTest @WebEndpointTest
void scrapeWithIncludedNames(WebTestClient client) { void scrapeWithIncludedNames(WebTestClient client) {
client.get().uri("/actuator/prometheus?includedNames=counter1_total,counter2_total").exchange().expectStatus() client.get().uri("/actuator/prometheus?includedNames=counter1_total,counter2_total").exchange().expectStatus()

Loading…
Cancel
Save