From 437a1601efe8bd7b9983bbeec27b360368bfe80b Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 4 Oct 2021 20:10:46 +0100 Subject: [PATCH] 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 --- .../export/prometheus/TextOutputFormat.java | 17 +++++++++++------ ...rometheusScrapeEndpointIntegrationTests.java | 8 ++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/prometheus/TextOutputFormat.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/prometheus/TextOutputFormat.java index 4831d37cf3..54b16b7110 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/prometheus/TextOutputFormat.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/prometheus/TextOutputFormat.java @@ -36,25 +36,30 @@ import org.springframework.util.MimeTypeUtils; public enum TextOutputFormat implements Producible { /** - * 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 void write(Writer writer, Enumeration 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 void write(Writer writer, Enumeration samples) throws IOException { - TextFormat.write004(writer, samples); + TextFormat.writeOpenMetrics100(writer, samples); } }; diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpointIntegrationTests.java index 9b0419efee..dbce312303 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpointIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpointIntegrationTests.java @@ -55,6 +55,14 @@ class PrometheusScrapeEndpointIntegrationTests { .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 void scrapeWithIncludedNames(WebTestClient client) { client.get().uri("/actuator/prometheus?includedNames=counter1_total,counter2_total").exchange().expectStatus()