Skip to content

Commit

Permalink
Use accept header to choose metics export format
Browse files Browse the repository at this point in the history
Fixes #30343

(cherry picked from commit 1191b16)
  • Loading branch information
jtama authored and gsmet committed Feb 1, 2023
1 parent 9e11b60 commit 9beeaf6
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public Consumer<Route> route() {
return new Consumer<Route>() {
@Override
public void accept(Route route) {
route.order(2).produces("application/json");
route.order(3).produces("application/json");
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.function.Consumer;

import io.prometheus.client.exporter.common.TextFormat;
import io.quarkus.micrometer.runtime.export.handlers.PrometheusHandler;
import io.quarkus.runtime.annotations.Recorder;
import io.vertx.core.Handler;
Expand All @@ -26,6 +27,7 @@ public Consumer<Route> route() {
@Override
public void accept(Route route) {
route.order(1).produces("text/plain");
route.order(2).produces(TextFormat.CONTENT_TYPE_OPENMETRICS_100);
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,33 @@ public void handle(RoutingContext routingContext) {
.setStatusMessage("Unable to resolve Prometheus registry instance");
} else {
ManagedContext requestContext = Arc.container().requestContext();
var acceptHeader = chooseContentType(routingContext.request().getHeader("Accept"));
if (requestContext.isActive()) {
doHandle(response);
doHandle(response, acceptHeader);
} else {
requestContext.activate();
try {
doHandle(response);
doHandle(response, acceptHeader);
} finally {
requestContext.terminate();
}
}
}
}

private void doHandle(HttpServerResponse response) {
response.putHeader("Content-Type", TextFormat.CONTENT_TYPE_OPENMETRICS_100)
.end(Buffer.buffer(registry.scrape(TextFormat.CONTENT_TYPE_OPENMETRICS_100)));
private String chooseContentType(String acceptHeader) {
if (acceptHeader == null) {
return TextFormat.CONTENT_TYPE_OPENMETRICS_100;
}
if (acceptHeader.startsWith("text/plain")) {
return TextFormat.CONTENT_TYPE_004;
}
return TextFormat.CONTENT_TYPE_OPENMETRICS_100;
}

private void doHandle(HttpServerResponse response, String acceptHeader) {
response.putHeader("Content-Type", acceptHeader)
.end(Buffer.buffer(registry.scrape(acceptHeader)));
}

private void setup() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

import io.prometheus.client.exporter.common.TextFormat;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;

/**
* Test functioning prometheus endpoint.
Expand Down Expand Up @@ -86,8 +88,102 @@ void testTemplatedPathOnClass() {

@Test
@Order(10)
void testPrometheusScrapeEndpoint() {
when().get("/q/metrics").then().statusCode(200)
void testPrometheusScrapeEndpointTextPlain() {
RestAssured.given().header("Accept", TextFormat.CONTENT_TYPE_004)
.when().get("/q/metrics")
.then().statusCode(200)

// Prometheus body has ALL THE THINGS in no particular order

.body(containsString("registry=\"prometheus\""))
.body(containsString("env=\"test\""))
.body(containsString("http_server_requests"))

.body(containsString("status=\"404\""))
.body(containsString("uri=\"NOT_FOUND\""))
.body(containsString("outcome=\"CLIENT_ERROR\""))

.body(containsString("status=\"500\""))
.body(containsString("uri=\"/message/fail\""))
.body(containsString("outcome=\"SERVER_ERROR\""))

.body(containsString("status=\"200\""))
.body(containsString("uri=\"/message\""))
.body(containsString("uri=\"/message/item/{id}\""))
.body(containsString("outcome=\"SUCCESS\""))
.body(containsString("uri=\"/message/match/{id}/{sub}\""))
.body(containsString("uri=\"/message/match/{other}\""))

.body(containsString(
"http_server_requests_seconds_count{env=\"test\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/template/path/{value}\""))

// Verify Hibernate Metrics
.body(containsString(
"hibernate_sessions_open_total{entityManagerFactory=\"<default>\",env=\"test\",registry=\"prometheus\",} 2.0"))
.body(containsString(
"hibernate_sessions_closed_total{entityManagerFactory=\"<default>\",env=\"test\",registry=\"prometheus\",} 2.0"))
.body(containsString(
"hibernate_connections_obtained_total{entityManagerFactory=\"<default>\",env=\"test\",registry=\"prometheus\",}"))
.body(containsString(
"hibernate_entities_inserts_total{entityManagerFactory=\"<default>\",env=\"test\",registry=\"prometheus\",} 3.0"))
.body(containsString(
"hibernate_flushes_total{entityManagerFactory=\"<default>\",env=\"test\",registry=\"prometheus\",} 1.0"))

// Annotated counters
.body(not(containsString("metric_none")))
.body(containsString(
"metric_all_total{class=\"io.quarkus.it.micrometer.prometheus.AnnotatedResource\",env=\"test\",exception=\"none\",extra=\"tag\",method=\"countAllInvocations\",registry=\"prometheus\",result=\"success\",} 1.0"))
.body(containsString(
"metric_all_total{class=\"io.quarkus.it.micrometer.prometheus.AnnotatedResource\",env=\"test\",exception=\"NullPointerException\",extra=\"tag\",method=\"countAllInvocations\",registry=\"prometheus\",result=\"failure\",} 1.0"))
.body(containsString(
"method_counted_total{class=\"io.quarkus.it.micrometer.prometheus.AnnotatedResource\",env=\"test\",exception=\"NullPointerException\",method=\"emptyMetricName\",registry=\"prometheus\",result=\"failure\",} 1.0"))
.body(containsString(
"method_counted_total{class=\"io.quarkus.it.micrometer.prometheus.AnnotatedResource\",env=\"test\",exception=\"none\",method=\"emptyMetricName\",registry=\"prometheus\",result=\"success\",} 1.0"))
.body(not(containsString("async_none")))
.body(containsString(
"async_all_total{class=\"io.quarkus.it.micrometer.prometheus.AnnotatedResource\",env=\"test\",exception=\"NullPointerException\",extra=\"tag\",method=\"countAllAsyncInvocations\",registry=\"prometheus\",result=\"failure\",} 1.0"))
.body(containsString(
"async_all_total{class=\"io.quarkus.it.micrometer.prometheus.AnnotatedResource\",env=\"test\",exception=\"none\",extra=\"tag\",method=\"countAllAsyncInvocations\",registry=\"prometheus\",result=\"success\",} 1.0"))
.body(containsString(
"method_counted_total{class=\"io.quarkus.it.micrometer.prometheus.AnnotatedResource\",env=\"test\",exception=\"NullPointerException\",method=\"emptyAsyncMetricName\",registry=\"prometheus\",result=\"failure\",} 1.0"))
.body(containsString(
"method_counted_total{class=\"io.quarkus.it.micrometer.prometheus.AnnotatedResource\",env=\"test\",exception=\"none\",method=\"emptyAsyncMetricName\",registry=\"prometheus\",result=\"success\",} 1.0"))

// Annotated Timers
.body(containsString(
"call_seconds_count{class=\"io.quarkus.it.micrometer.prometheus.AnnotatedResource\",env=\"test\",exception=\"NullPointerException\",extra=\"tag\",method=\"call\",registry=\"prometheus\",} 1.0"))
.body(containsString(
"call_seconds_count{class=\"io.quarkus.it.micrometer.prometheus.AnnotatedResource\",env=\"test\",exception=\"none\",extra=\"tag\",method=\"call\",registry=\"prometheus\",}"))
.body(containsString(
"async_call_seconds_count{class=\"io.quarkus.it.micrometer.prometheus.AnnotatedResource\",env=\"test\",exception=\"NullPointerException\",extra=\"tag\",method=\"asyncCall\",registry=\"prometheus\",} 1.0"))
.body(containsString(
"async_call_seconds_count{class=\"io.quarkus.it.micrometer.prometheus.AnnotatedResource\",env=\"test\",exception=\"none\",extra=\"tag\",method=\"asyncCall\",registry=\"prometheus\",} 1.0"))
.body(containsString(
"longCall_seconds_active_count{class=\"io.quarkus.it.micrometer.prometheus.AnnotatedResource\",env=\"test\",extra=\"tag\",method=\"longCall\",registry=\"prometheus\",}"))
.body(containsString(
"async_longCall_seconds_duration_sum{class=\"io.quarkus.it.micrometer.prometheus.AnnotatedResource\",env=\"test\",extra=\"tag\",method=\"longAsyncCall\",registry=\"prometheus\",} 0.0"))

// Configured median, 95th percentile and histogram buckets
.body(containsString(
"prime_number_test_seconds{env=\"test\",registry=\"prometheus\",quantile=\"0.5\",}"))
.body(containsString(
"prime_number_test_seconds{env=\"test\",registry=\"prometheus\",quantile=\"0.95\",}"))
.body(containsString(
"prime_number_test_seconds_bucket{env=\"test\",registry=\"prometheus\",le=\"0.001\",}"))

// this was defined by a tag to a non-matching registry, and should not be found
.body(not(containsString("class-should-not-match")))

// should not find this ignored uri
.body(not(containsString("uri=\"/fruit/create\"")));
}

@Test
@Order(11)
void testPrometheusScrapeEndpointOpenMetrics() {
RestAssured.given().header("Accept", TextFormat.CONTENT_TYPE_OPENMETRICS_100)
.when().get("/q/metrics")
.then().statusCode(200)

// Prometheus body has ALL THE THINGS in no particular order

Expand Down

0 comments on commit 9beeaf6

Please sign in to comment.