Skip to content

Commit f4f1a00

Browse files
committed
Feature: Add auto-config for Prometheus Pushgateway. Fixes #14346
Signed-off-by: David J. M. Karlsen <[email protected]>
1 parent 0493355 commit f4f1a00

File tree

5 files changed

+252
-0
lines changed

5 files changed

+252
-0
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@
132132
<artifactId>micrometer-registry-prometheus</artifactId>
133133
<optional>true</optional>
134134
</dependency>
135+
<dependency>
136+
<groupId>io.prometheus</groupId>
137+
<artifactId>simpleclient_pushgateway</artifactId>
138+
<optional>true</optional>
139+
</dependency>
135140
<dependency>
136141
<groupId>io.micrometer</groupId>
137142
<artifactId>micrometer-registry-signalfx</artifactId>

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

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
import io.micrometer.prometheus.PrometheusMeterRegistry;
2222
import io.prometheus.client.CollectorRegistry;
2323

24+
import io.prometheus.client.exporter.PushGateway;
25+
import org.slf4j.Logger;
26+
import org.slf4j.LoggerFactory;
2427
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
2528
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
2629
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
@@ -36,6 +39,13 @@
3639
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3740
import org.springframework.context.annotation.Bean;
3841
import org.springframework.context.annotation.Configuration;
42+
import org.springframework.core.env.Environment;
43+
44+
import javax.annotation.PreDestroy;
45+
import java.net.UnknownHostException;
46+
import java.util.concurrent.Executors;
47+
import java.util.concurrent.ScheduledExecutorService;
48+
import java.util.concurrent.TimeUnit;
3949

4050
/**
4151
* {@link EnableAutoConfiguration Auto-configuration} for exporting metrics to Prometheus.
@@ -86,4 +96,107 @@ public PrometheusScrapeEndpoint prometheusEndpoint(
8696

8797
}
8898

99+
/**
100+
* Configuration for <a href="https://github.com/prometheus/pushgateway">Prometheus
101+
* Pushgateway</a>.
102+
*
103+
* @author David J. M. Karlsen
104+
*/
105+
@Configuration
106+
@ConditionalOnClass(PushGateway.class)
107+
@ConditionalOnProperty(prefix = "management.metrics.export.prometheus.pushgateway", name = "enabled", havingValue = "true", matchIfMissing = false)
108+
public static class PrometheusPushGatewayConfiguration {
109+
110+
@Bean
111+
public PushGatewayHandler pushGatewayHandler(CollectorRegistry collectorRegistry,
112+
PrometheusProperties prometheusProperties, Environment environment) {
113+
return new PushGatewayHandler(collectorRegistry, prometheusProperties,
114+
environment);
115+
}
116+
117+
public static class PushGatewayHandler {
118+
119+
private final Logger logger = LoggerFactory
120+
.getLogger(PrometheusPushGatewayConfiguration.class);
121+
122+
private final CollectorRegistry collectorRegistry;
123+
124+
private final PrometheusProperties.PushgatewayProperties pushgatewayProperties;
125+
126+
private final PushGateway pushGateway;
127+
128+
private final Environment environment;
129+
130+
private final ScheduledExecutorService executorService;
131+
132+
public PushGatewayHandler(CollectorRegistry collectorRegistry,
133+
PrometheusProperties prometheusProperties, Environment environment) {
134+
this.collectorRegistry = collectorRegistry;
135+
this.pushgatewayProperties = prometheusProperties.getPushgateway();
136+
this.pushGateway = new PushGateway(pushgatewayProperties.getBaseUrl());
137+
this.environment = environment;
138+
this.executorService = Executors.newSingleThreadScheduledExecutor((r) -> {
139+
final Thread thread = new Thread(r);
140+
thread.setDaemon(true);
141+
thread.setName("micrometer-pushgateway");
142+
return thread;
143+
});
144+
executorService.scheduleAtFixedRate(this::push, 0,
145+
pushgatewayProperties.getPushRate().toMillis(),
146+
TimeUnit.MILLISECONDS);
147+
}
148+
149+
void push() {
150+
try {
151+
pushGateway.pushAdd(collectorRegistry, job(),
152+
pushgatewayProperties.getGroupingKeys());
153+
}
154+
catch (UnknownHostException e) {
155+
logger.error("Unable to locate host '"
156+
+ pushgatewayProperties.getBaseUrl()
157+
+ "'. No longer attempting metrics publication to this host");
158+
executorService.shutdown();
159+
}
160+
catch (Throwable t) {
161+
logger.error("Unable to push metrics to Prometheus Pushgateway", t);
162+
}
163+
}
164+
165+
@PreDestroy
166+
void shutdown() {
167+
executorService.shutdown();
168+
if (pushgatewayProperties.isPushOnShutdown()) {
169+
push();
170+
}
171+
if (pushgatewayProperties.isDeleteOnShutdown()) {
172+
try {
173+
pushGateway.delete(job(),
174+
pushgatewayProperties.getGroupingKeys());
175+
}
176+
catch (Throwable t) {
177+
logger.error(
178+
"Unable to delete metrics from Prometheus Pushgateway",
179+
t);
180+
}
181+
}
182+
}
183+
184+
private String job() {
185+
String job = pushgatewayProperties.getJob();
186+
if (job == null) {
187+
job = environment.getProperty("spring.application.name");
188+
}
189+
if (job == null) {
190+
// There's a history of Prometheus spring integration defaulting the
191+
// job name to "spring" from when
192+
// Prometheus integration didn't exist in Spring itself.
193+
job = "spring";
194+
}
195+
return job;
196+
}
197+
198+
}
199+
200+
}
201+
89202
}

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

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus;
1818

1919
import java.time.Duration;
20+
import java.util.HashMap;
21+
import java.util.Map;
2022

2123
import org.springframework.boot.context.properties.ConfigurationProperties;
2224

@@ -36,6 +38,12 @@ public class PrometheusProperties {
3638
*/
3739
private boolean descriptions = true;
3840

41+
/**
42+
* Configuration options for using Prometheus Pushgateway, allowing metrics to be
43+
* pushed when they cannot be scraped.
44+
*/
45+
private PushgatewayProperties pushgateway = new PushgatewayProperties();
46+
3947
/**
4048
* Step size (i.e. reporting frequency) to use.
4149
*/
@@ -57,4 +65,110 @@ public void setStep(Duration step) {
5765
this.step = step;
5866
}
5967

68+
public PushgatewayProperties getPushgateway() {
69+
return pushgateway;
70+
}
71+
72+
public void setPushgateway(PushgatewayProperties pushgateway) {
73+
this.pushgateway = pushgateway;
74+
}
75+
76+
/**
77+
* Configuration options for push-based interaction with Prometheus.
78+
*/
79+
public static class PushgatewayProperties {
80+
81+
/**
82+
* Enable publishing via a Prometheus Pushgateway.
83+
*/
84+
private Boolean enabled = false;
85+
86+
/**
87+
* Required host:port or ip:port of the Pushgateway.
88+
*/
89+
private String baseUrl = "localhost:9091";
90+
91+
/**
92+
* Required identifier for this application instance.
93+
*/
94+
private String job;
95+
96+
/**
97+
* Frequency with which to push metrics to Pushgateway.
98+
*/
99+
private Duration pushRate = Duration.ofMinutes(1);
100+
101+
/**
102+
* Push metrics right before shut-down. Mostly useful for batch jobs.
103+
*/
104+
private boolean pushOnShutdown = true;
105+
106+
/**
107+
* Delete metrics from Pushgateway when application is shut-down
108+
*/
109+
private boolean deleteOnShutdown = true;
110+
111+
/**
112+
* Used to group metrics in pushgateway. A common example is setting
113+
*/
114+
private Map<String, String> groupingKeys = new HashMap<>();
115+
116+
public Boolean getEnabled() {
117+
return enabled;
118+
}
119+
120+
public void setEnabled(Boolean enabled) {
121+
this.enabled = enabled;
122+
}
123+
124+
public String getBaseUrl() {
125+
return baseUrl;
126+
}
127+
128+
public void setBaseUrl(String baseUrl) {
129+
this.baseUrl = baseUrl;
130+
}
131+
132+
public String getJob() {
133+
return job;
134+
}
135+
136+
public void setJob(String job) {
137+
this.job = job;
138+
}
139+
140+
public Duration getPushRate() {
141+
return pushRate;
142+
}
143+
144+
public void setPushRate(Duration pushRate) {
145+
this.pushRate = pushRate;
146+
}
147+
148+
public boolean isPushOnShutdown() {
149+
return pushOnShutdown;
150+
}
151+
152+
public void setPushOnShutdown(boolean pushOnShutdown) {
153+
this.pushOnShutdown = pushOnShutdown;
154+
}
155+
156+
public boolean isDeleteOnShutdown() {
157+
return deleteOnShutdown;
158+
}
159+
160+
public void setDeleteOnShutdown(boolean deleteOnShutdown) {
161+
this.deleteOnShutdown = deleteOnShutdown;
162+
}
163+
164+
public Map<String, String> getGroupingKeys() {
165+
return groupingKeys;
166+
}
167+
168+
public void setGroupingKeys(Map<String, String> groupingKeys) {
169+
this.groupingKeys = groupingKeys;
170+
}
171+
172+
}
173+
60174
}

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.context.annotation.Import;
3232

3333
import static org.assertj.core.api.Assertions.assertThat;
34+
import static org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusMetricsExportAutoConfiguration.*;
3435

3536
/**
3637
* Tests for {@link PrometheusMetricsExportAutoConfiguration}.
@@ -128,6 +129,18 @@ public void allowsCustomScrapeEndpointToBeUsed() {
128129
.hasSingleBean(PrometheusScrapeEndpoint.class));
129130
}
130131

132+
@Test
133+
public void withPushGatewayEnabled() {
134+
this.contextRunner
135+
.withConfiguration(
136+
AutoConfigurations.of(ManagementContextAutoConfiguration.class))
137+
.withPropertyValues(
138+
"management.metrics.export.prometheus.pushgateway.enabled=true")
139+
.withUserConfiguration(BaseConfiguration.class)
140+
.run((context) -> assertThat(context).hasSingleBean(
141+
PrometheusPushGatewayConfiguration.PushGatewayHandler.class));
142+
}
143+
131144
@Configuration
132145
static class BaseConfiguration {
133146

spring-boot-project/spring-boot-dependencies/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@
140140
<nio-multipart-parser.version>1.1.0</nio-multipart-parser.version>
141141
<pooled-jms-version>1.0.3</pooled-jms-version>
142142
<postgresql.version>42.2.4</postgresql.version>
143+
<!-- need to take care that this version harmonizes with micrometer ones -->
144+
<prometheus-pushgateway.version>0.5.0</prometheus-pushgateway.version>
143145
<quartz.version>2.3.0</quartz.version>
144146
<querydsl.version>4.2.1</querydsl.version>
145147
<rabbit-amqp-client.version>5.4.1</rabbit-amqp-client.version>
@@ -941,6 +943,11 @@
941943
<artifactId>netty-tcnative-boringssl-static</artifactId>
942944
<version>${netty-tcnative.version}</version>
943945
</dependency>
946+
<dependency>
947+
<groupId>io.prometheus</groupId>
948+
<artifactId>simpleclient_pushgateway</artifactId>
949+
<version>${prometheus-pushgateway.version}</version>
950+
</dependency>
944951
<dependency>
945952
<groupId>io.projectreactor</groupId>
946953
<artifactId>reactor-bom</artifactId>

0 commit comments

Comments
 (0)