Skip to content

Commit 36ef68b

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 36ef68b

File tree

5 files changed

+232
-0
lines changed

5 files changed

+232
-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: 96 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,90 @@ public PrometheusScrapeEndpoint prometheusEndpoint(
8696

8797
}
8898

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

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

Lines changed: 113 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 pushed
43+
* 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,109 @@ 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+
/**
78+
* Configuration options for push-based interaction with Prometheus.
79+
*/
80+
public static class PushgatewayProperties {
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+
60173
}

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

Lines changed: 12 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,17 @@ 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("management.metrics.export.prometheus.pushgateway.enabled=true")
138+
.withUserConfiguration(BaseConfiguration.class)
139+
.run((context) -> assertThat(context)
140+
.hasSingleBean(PrometheusPushGatewayConfiguration.PushGatewayHandler.class));
141+
}
142+
131143
@Configuration
132144
static class BaseConfiguration {
133145

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,12 @@
941941
<artifactId>netty-tcnative-boringssl-static</artifactId>
942942
<version>${netty-tcnative.version}</version>
943943
</dependency>
944+
<!-- need to take care that this version harmonizes with micrometer ones -->
945+
<dependency>
946+
<groupId>io.prometheus</groupId>
947+
<artifactId>simpleclient_pushgateway</artifactId>
948+
<version>0.5.0</version>
949+
</dependency>
944950
<dependency>
945951
<groupId>io.projectreactor</groupId>
946952
<artifactId>reactor-bom</artifactId>

0 commit comments

Comments
 (0)