Skip to content

Commit cd1baf1

Browse files
izeyesnicoll
authored andcommitted
Support filtered scrape for Prometheus
See gh-21545
1 parent c848f80 commit cd1baf1

File tree

4 files changed

+57
-7
lines changed

4 files changed

+57
-7
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/prometheus.adoc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,14 @@ include::{snippets}/prometheus/curl-request.adoc[]
1515
The resulting response is similar to the following:
1616

1717
include::{snippets}/prometheus/http-response.adoc[]
18+
19+
20+
21+
[[prometheus-retrieving-query-parameters]]
22+
=== Query Parameters
23+
24+
The endpoint uses query parameters to limit the samples that it returns.
25+
The following table shows the supported query parameters:
26+
27+
[cols="2,4"]
28+
include::{snippets}/prometheus/request-parameters.adoc[]

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/PrometheusScrapeEndpointDocumentationTests.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,13 +28,16 @@
2828
import org.springframework.context.annotation.Import;
2929

3030
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
31+
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
32+
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
3133
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
3234
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
3335

3436
/**
3537
* Tests for generating documentation describing the {@link PrometheusScrapeEndpoint}.
3638
*
3739
* @author Andy Wilkinson
40+
* @author Johnny Lim
3841
*/
3942
class PrometheusScrapeEndpointDocumentationTests extends MockMvcEndpointDocumentationTests {
4043

@@ -43,6 +46,16 @@ void prometheus() throws Exception {
4346
this.mockMvc.perform(get("/actuator/prometheus")).andExpect(status().isOk()).andDo(document("prometheus"));
4447
}
4548

49+
@Test
50+
void filteredPrometheus() throws Exception {
51+
this.mockMvc
52+
.perform(get("/actuator/prometheus").param("includedNames",
53+
"jvm_memory_used_bytes,jvm_memory_committed_bytes"))
54+
.andExpect(status().isOk())
55+
.andDo(document("prometheus", requestParameters(parameterWithName("includedNames")
56+
.description("Restricts the samples to those that match the names. Optional.").optional())));
57+
}
58+
4659
@Configuration(proxyBeanMethods = false)
4760
@Import(BaseDocumentationConfiguration.class)
4861
static class TestConfiguration {

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpoint.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,19 +19,24 @@
1919
import java.io.IOException;
2020
import java.io.StringWriter;
2121
import java.io.Writer;
22+
import java.util.Enumeration;
23+
import java.util.Set;
2224

25+
import io.prometheus.client.Collector.MetricFamilySamples;
2326
import io.prometheus.client.CollectorRegistry;
2427
import io.prometheus.client.exporter.common.TextFormat;
2528

2629
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
2730
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
2831
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint;
32+
import org.springframework.lang.Nullable;
2933

3034
/**
3135
* {@link Endpoint @Endpoint} that outputs metrics in a format that can be scraped by the
3236
* Prometheus server.
3337
*
3438
* @author Jon Schneider
39+
* @author Johnny Lim
3540
* @since 2.0.0
3641
*/
3742
@WebEndpoint(id = "prometheus")
@@ -44,10 +49,13 @@ public PrometheusScrapeEndpoint(CollectorRegistry collectorRegistry) {
4449
}
4550

4651
@ReadOperation(produces = TextFormat.CONTENT_TYPE_004)
47-
public String scrape() {
52+
public String scrape(@Nullable Set<String> includedNames) {
4853
try {
4954
Writer writer = new StringWriter();
50-
TextFormat.write004(writer, this.collectorRegistry.metricFamilySamples());
55+
Enumeration<MetricFamilySamples> samples = (includedNames != null)
56+
? this.collectorRegistry.filteredMetricFamilySamples(includedNames)
57+
: this.collectorRegistry.metricFamilySamples();
58+
TextFormat.write004(writer, samples);
5159
return writer.toString();
5260
}
5361
catch (IOException ex) {

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpointIntegrationTests.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
1717
package org.springframework.boot.actuate.metrics.export.prometheus;
1818

1919
import io.micrometer.core.instrument.Clock;
20+
import io.micrometer.core.instrument.Counter;
2021
import io.micrometer.core.instrument.MeterRegistry;
2122
import io.micrometer.prometheus.PrometheusMeterRegistry;
2223
import io.prometheus.client.CollectorRegistry;
@@ -28,17 +29,30 @@
2829
import org.springframework.http.MediaType;
2930
import org.springframework.test.web.reactive.server.WebTestClient;
3031

32+
import static org.assertj.core.api.Assertions.assertThat;
33+
3134
/**
3235
* Tests for {@link PrometheusScrapeEndpoint}.
3336
*
3437
* @author Jon Schneider
38+
* @author Johnny Lim
3539
*/
3640
class PrometheusScrapeEndpointIntegrationTests {
3741

3842
@WebEndpointTest
3943
void scrapeHasContentTypeText004(WebTestClient client) {
4044
client.get().uri("/actuator/prometheus").exchange().expectStatus().isOk().expectHeader()
41-
.contentType(MediaType.parseMediaType(TextFormat.CONTENT_TYPE_004));
45+
.contentType(MediaType.parseMediaType(TextFormat.CONTENT_TYPE_004)).expectBody(String.class)
46+
.value((body) -> assertThat(body).contains("counter1_total").contains("counter2_total")
47+
.contains("counter3_total"));
48+
}
49+
50+
@WebEndpointTest
51+
void scrapeWithIncludedNames(WebTestClient client) {
52+
client.get().uri("/actuator/prometheus?includedNames=counter1_total,counter2_total").exchange().expectStatus()
53+
.isOk().expectHeader().contentType(MediaType.parseMediaType(TextFormat.CONTENT_TYPE_004))
54+
.expectBody(String.class).value((body) -> assertThat(body).contains("counter1_total")
55+
.contains("counter2_total").doesNotContain("counter3_total"));
4256
}
4357

4458
@Configuration(proxyBeanMethods = false)
@@ -56,7 +70,11 @@ CollectorRegistry collectorRegistry() {
5670

5771
@Bean
5872
MeterRegistry registry(CollectorRegistry registry) {
59-
return new PrometheusMeterRegistry((k) -> null, registry, Clock.SYSTEM);
73+
PrometheusMeterRegistry meterRegistry = new PrometheusMeterRegistry((k) -> null, registry, Clock.SYSTEM);
74+
Counter.builder("counter1").register(meterRegistry);
75+
Counter.builder("counter2").register(meterRegistry);
76+
Counter.builder("counter3").register(meterRegistry);
77+
return meterRegistry;
6078
}
6179

6280
}

0 commit comments

Comments
 (0)