diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/prometheus.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/prometheus.adoc index 16d2205ba4ae..f79085a588ad 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/prometheus.adoc +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/prometheus.adoc @@ -15,3 +15,14 @@ include::{snippets}/prometheus/curl-request.adoc[] The resulting response is similar to the following: include::{snippets}/prometheus/http-response.adoc[] + + + +[[prometheus-retrieving-query-parameters]] +=== Query Parameters + +The endpoint uses query parameters to limit the samples that it returns. +The following table shows the supported query parameters: + +[cols="2,4"] +include::{snippets}/prometheus/request-parameters.adoc[] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/PrometheusScrapeEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/PrometheusScrapeEndpointDocumentationTests.java index c72db1e99e0a..a1e1e36b037b 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/PrometheusScrapeEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/PrometheusScrapeEndpointDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,8 @@ import org.springframework.context.annotation.Import; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -35,6 +37,7 @@ * Tests for generating documentation describing the {@link PrometheusScrapeEndpoint}. * * @author Andy Wilkinson + * @author Johnny Lim */ class PrometheusScrapeEndpointDocumentationTests extends MockMvcEndpointDocumentationTests { @@ -43,6 +46,16 @@ void prometheus() throws Exception { this.mockMvc.perform(get("/actuator/prometheus")).andExpect(status().isOk()).andDo(document("prometheus")); } + @Test + void filteredPrometheus() throws Exception { + this.mockMvc + .perform(get("/actuator/prometheus").param("includedNames", + "jvm_memory_used_bytes,jvm_memory_committed_bytes")) + .andExpect(status().isOk()) + .andDo(document("prometheus", requestParameters(parameterWithName("includedNames") + .description("Restricts the samples to those that match the names. Optional.").optional()))); + } + @Configuration(proxyBeanMethods = false) @Import(BaseDocumentationConfiguration.class) static class TestConfiguration { diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpoint.java index 48afc50f4eba..6624f2938989 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,19 +19,24 @@ import java.io.IOException; import java.io.StringWriter; import java.io.Writer; +import java.util.Enumeration; +import java.util.Set; +import io.prometheus.client.Collector.MetricFamilySamples; import io.prometheus.client.CollectorRegistry; import io.prometheus.client.exporter.common.TextFormat; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint; +import org.springframework.lang.Nullable; /** * {@link Endpoint @Endpoint} that outputs metrics in a format that can be scraped by the * Prometheus server. * * @author Jon Schneider + * @author Johnny Lim * @since 2.0.0 */ @WebEndpoint(id = "prometheus") @@ -44,10 +49,13 @@ public PrometheusScrapeEndpoint(CollectorRegistry collectorRegistry) { } @ReadOperation(produces = TextFormat.CONTENT_TYPE_004) - public String scrape() { + public String scrape(@Nullable Set includedNames) { try { Writer writer = new StringWriter(); - TextFormat.write004(writer, this.collectorRegistry.metricFamilySamples()); + Enumeration samples = (includedNames != null) + ? this.collectorRegistry.filteredMetricFamilySamples(includedNames) + : this.collectorRegistry.metricFamilySamples(); + TextFormat.write004(writer, samples); return writer.toString(); } catch (IOException ex) { 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 6c3e3243409b..4ece6adf0ffc 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.boot.actuate.metrics.export.prometheus; import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.prometheus.PrometheusMeterRegistry; import io.prometheus.client.CollectorRegistry; @@ -28,17 +29,30 @@ import org.springframework.http.MediaType; import org.springframework.test.web.reactive.server.WebTestClient; +import static org.assertj.core.api.Assertions.assertThat; + /** * Tests for {@link PrometheusScrapeEndpoint}. * * @author Jon Schneider + * @author Johnny Lim */ class PrometheusScrapeEndpointIntegrationTests { @WebEndpointTest void scrapeHasContentTypeText004(WebTestClient client) { client.get().uri("/actuator/prometheus").exchange().expectStatus().isOk().expectHeader() - .contentType(MediaType.parseMediaType(TextFormat.CONTENT_TYPE_004)); + .contentType(MediaType.parseMediaType(TextFormat.CONTENT_TYPE_004)).expectBody(String.class) + .value((body) -> assertThat(body).contains("counter1_total").contains("counter2_total") + .contains("counter3_total")); + } + + @WebEndpointTest + void scrapeWithIncludedNames(WebTestClient client) { + client.get().uri("/actuator/prometheus?includedNames=counter1_total,counter2_total").exchange().expectStatus() + .isOk().expectHeader().contentType(MediaType.parseMediaType(TextFormat.CONTENT_TYPE_004)) + .expectBody(String.class).value((body) -> assertThat(body).contains("counter1_total") + .contains("counter2_total").doesNotContain("counter3_total")); } @Configuration(proxyBeanMethods = false) @@ -56,7 +70,11 @@ CollectorRegistry collectorRegistry() { @Bean MeterRegistry registry(CollectorRegistry registry) { - return new PrometheusMeterRegistry((k) -> null, registry, Clock.SYSTEM); + PrometheusMeterRegistry meterRegistry = new PrometheusMeterRegistry((k) -> null, registry, Clock.SYSTEM); + Counter.builder("counter1").register(meterRegistry); + Counter.builder("counter2").register(meterRegistry); + Counter.builder("counter3").register(meterRegistry); + return meterRegistry; } }