Skip to content

Commit 20ecf73

Browse files
committed
Polish "Add Prometheus push gateway support"
Rework Prometheus push gateway support so that the central class can be used outside of auto-configuration. The shutdown flags have also been replaced with a single "shutdown-operation" property since it's unlikely that both "push" and "delete" will be required. It's also possible now to supply a `TaskScheduler` to the manager. See gh-14353
1 parent 4e71981 commit 20ecf73

File tree

8 files changed

+488
-142
lines changed

8 files changed

+488
-142
lines changed

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

Lines changed: 31 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,21 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus;
1818

19-
import java.net.UnknownHostException;
20-
import java.util.concurrent.Executors;
21-
import java.util.concurrent.ScheduledExecutorService;
22-
import java.util.concurrent.TimeUnit;
23-
24-
import javax.annotation.PreDestroy;
19+
import java.time.Duration;
20+
import java.util.Map;
2521

2622
import io.micrometer.core.instrument.Clock;
2723
import io.micrometer.prometheus.PrometheusConfig;
2824
import io.micrometer.prometheus.PrometheusMeterRegistry;
2925
import io.prometheus.client.CollectorRegistry;
3026
import io.prometheus.client.exporter.PushGateway;
31-
import org.slf4j.Logger;
32-
import org.slf4j.LoggerFactory;
3327

3428
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
3529
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
3630
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
3731
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
32+
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager;
33+
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager.ShutdownOperation;
3834
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint;
3935
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
4036
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
@@ -53,6 +49,7 @@
5349
*
5450
* @since 2.0.0
5551
* @author Jon Schneider
52+
* @author David J. M. Karlsen
5653
*/
5754
@Configuration
5855
@AutoConfigureBefore({ CompositeMeterRegistryAutoConfiguration.class,
@@ -100,108 +97,42 @@ public PrometheusScrapeEndpoint prometheusEndpoint(
10097
/**
10198
* Configuration for <a href="https://github.com/prometheus/pushgateway">Prometheus
10299
* Pushgateway</a>.
103-
*
104-
* @author David J. M. Karlsen
105100
*/
106101
@Configuration
107102
@ConditionalOnClass(PushGateway.class)
108103
@ConditionalOnProperty(prefix = "management.metrics.export.prometheus.pushgateway", name = "enabled")
109104
public static class PrometheusPushGatewayConfiguration {
110105

106+
/**
107+
* The fallback job name. We use 'spring' since there's a history of Prometheus
108+
* spring integration defaulting to that name from when Prometheus integration
109+
* didn't exist in Spring itself.
110+
*/
111+
private static final String FALLBACK_JOB = "spring";
112+
111113
@Bean
112-
public PushGatewayHandler pushGatewayHandler(CollectorRegistry collectorRegistry,
114+
@ConditionalOnMissingBean
115+
public PrometheusPushGatewayManager prometheusPushGatewayManager(
116+
CollectorRegistry collectorRegistry,
113117
PrometheusProperties prometheusProperties, Environment environment) {
114-
return new PushGatewayHandler(collectorRegistry, prometheusProperties,
115-
environment);
118+
PrometheusProperties.Pushgateway properties = prometheusProperties
119+
.getPushgateway();
120+
PushGateway pushGateway = new PushGateway(properties.getBaseUrl());
121+
Duration pushRate = properties.getPushRate();
122+
String job = getJob(properties, environment);
123+
Map<String, String> groupingKey = properties.getGroupingKey();
124+
ShutdownOperation shutdownOperation = properties.getShutdownOperation();
125+
return new PrometheusPushGatewayManager(pushGateway, collectorRegistry,
126+
pushRate, job, groupingKey, shutdownOperation);
116127
}
117128

118-
static class PushGatewayHandler {
119-
120-
private final Logger logger = LoggerFactory
121-
.getLogger(PrometheusPushGatewayConfiguration.class);
122-
123-
private final CollectorRegistry collectorRegistry;
124-
125-
private final PrometheusProperties.PushgatewayProperties pushgatewayProperties;
126-
127-
private final PushGateway pushGateway;
128-
129-
private final Environment environment;
130-
131-
private final ScheduledExecutorService executorService;
132-
133-
PushGatewayHandler(CollectorRegistry collectorRegistry,
134-
PrometheusProperties prometheusProperties, Environment environment) {
135-
this.collectorRegistry = collectorRegistry;
136-
this.pushgatewayProperties = prometheusProperties.getPushgateway();
137-
this.pushGateway = new PushGateway(
138-
this.pushgatewayProperties.getBaseUrl());
139-
this.environment = environment;
140-
this.executorService = Executors.newSingleThreadScheduledExecutor((r) -> {
141-
Thread thread = new Thread(r);
142-
thread.setDaemon(true);
143-
thread.setName("micrometer-pushgateway");
144-
return thread;
145-
});
146-
this.executorService.scheduleAtFixedRate(this::push, 0,
147-
this.pushgatewayProperties.getPushRate().toMillis(),
148-
TimeUnit.MILLISECONDS);
149-
}
150-
151-
void push() {
152-
try {
153-
this.pushGateway.pushAdd(this.collectorRegistry, getJobName(),
154-
this.pushgatewayProperties.getGroupingKeys());
155-
}
156-
catch (UnknownHostException ex) {
157-
this.logger.error("Unable to locate host '"
158-
+ this.pushgatewayProperties.getBaseUrl()
159-
+ "'. No longer attempting metrics publication to this host");
160-
this.executorService.shutdown();
161-
}
162-
catch (Throwable throwable) {
163-
this.logger.error("Unable to push metrics to Prometheus Pushgateway",
164-
throwable);
165-
}
166-
}
167-
168-
@PreDestroy
169-
void shutdown() {
170-
this.executorService.shutdown();
171-
if (this.pushgatewayProperties.isPushOnShutdown()) {
172-
push();
173-
}
174-
if (this.pushgatewayProperties.isDeleteOnShutdown()) {
175-
delete();
176-
}
177-
}
178-
179-
private void delete() {
180-
try {
181-
this.pushGateway.delete(getJobName(),
182-
this.pushgatewayProperties.getGroupingKeys());
183-
}
184-
catch (Throwable throwable) {
185-
this.logger.error(
186-
"Unable to delete metrics from Prometheus Pushgateway",
187-
throwable);
188-
}
189-
}
190-
191-
private String getJobName() {
192-
String job = this.pushgatewayProperties.getJob();
193-
if (job == null) {
194-
job = this.environment.getProperty("spring.application.name");
195-
}
196-
if (job == null) {
197-
// There's a history of Prometheus spring integration defaulting the
198-
// getJobName name to "spring" from when
199-
// Prometheus integration didn't exist in Spring itself.
200-
job = "spring";
201-
}
202-
return job;
203-
}
204-
129+
private String getJob(PrometheusProperties.Pushgateway properties,
130+
Environment environment) {
131+
String job = properties.getJob();
132+
job = (job != null) ? job
133+
: environment.getProperty("spring.application.name");
134+
job = (job != null) ? job : FALLBACK_JOB;
135+
return job;
205136
}
206137

207138
}

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

Lines changed: 25 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.HashMap;
2121
import java.util.Map;
2222

23+
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager.ShutdownOperation;
2324
import org.springframework.boot.context.properties.ConfigurationProperties;
2425

2526
/**
@@ -42,7 +43,7 @@ public class PrometheusProperties {
4243
* Configuration options for using Prometheus Pushgateway, allowing metrics to be
4344
* pushed when they cannot be scraped.
4445
*/
45-
private PushgatewayProperties pushgateway = new PushgatewayProperties();
46+
private Pushgateway pushgateway = new Pushgateway();
4647

4748
/**
4849
* Step size (i.e. reporting frequency) to use.
@@ -65,53 +66,48 @@ public void setStep(Duration step) {
6566
this.step = step;
6667
}
6768

68-
public PushgatewayProperties getPushgateway() {
69+
public Pushgateway getPushgateway() {
6970
return this.pushgateway;
7071
}
7172

72-
public void setPushgateway(PushgatewayProperties pushgateway) {
73+
public void setPushgateway(Pushgateway pushgateway) {
7374
this.pushgateway = pushgateway;
7475
}
7576

7677
/**
7778
* Configuration options for push-based interaction with Prometheus.
7879
*/
79-
public static class PushgatewayProperties {
80+
public static class Pushgateway {
8081

8182
/**
8283
* Enable publishing via a Prometheus Pushgateway.
8384
*/
8485
private Boolean enabled = false;
8586

8687
/**
87-
* Required host:port or ip:port of the Pushgateway.
88+
* Base URL for the Pushgateway.
8889
*/
8990
private String baseUrl = "localhost:9091";
9091

9192
/**
92-
* Required identifier for this application instance.
93-
*/
94-
private String job;
95-
96-
/**
97-
* Frequency with which to push metrics to Pushgateway.
93+
* Frequency with which to push metrics.
9894
*/
9995
private Duration pushRate = Duration.ofMinutes(1);
10096

10197
/**
102-
* Push metrics right before shut-down. Mostly useful for batch jobs.
98+
* Job identifier for this application instance.
10399
*/
104-
private boolean pushOnShutdown = true;
100+
private String job;
105101

106102
/**
107-
* Delete metrics from Pushgateway when application is shut-down.
103+
* Grouping key for the pushed metrics.
108104
*/
109-
private boolean deleteOnShutdown = true;
105+
private Map<String, String> groupingKey = new HashMap<>();
110106

111107
/**
112-
* Used to group metrics in pushgateway. A common example is setting
108+
* Operation that should be performed on shutdown.
113109
*/
114-
private Map<String, String> groupingKeys = new HashMap<>();
110+
private ShutdownOperation shutdownOperation = ShutdownOperation.NONE;
115111

116112
public Boolean getEnabled() {
117113
return this.enabled;
@@ -129,14 +125,6 @@ public void setBaseUrl(String baseUrl) {
129125
this.baseUrl = baseUrl;
130126
}
131127

132-
public String getJob() {
133-
return this.job;
134-
}
135-
136-
public void setJob(String job) {
137-
this.job = job;
138-
}
139-
140128
public Duration getPushRate() {
141129
return this.pushRate;
142130
}
@@ -145,28 +133,28 @@ public void setPushRate(Duration pushRate) {
145133
this.pushRate = pushRate;
146134
}
147135

148-
public boolean isPushOnShutdown() {
149-
return this.pushOnShutdown;
136+
public String getJob() {
137+
return this.job;
150138
}
151139

152-
public void setPushOnShutdown(boolean pushOnShutdown) {
153-
this.pushOnShutdown = pushOnShutdown;
140+
public void setJob(String job) {
141+
this.job = job;
154142
}
155143

156-
public boolean isDeleteOnShutdown() {
157-
return this.deleteOnShutdown;
144+
public Map<String, String> getGroupingKey() {
145+
return this.groupingKey;
158146
}
159147

160-
public void setDeleteOnShutdown(boolean deleteOnShutdown) {
161-
this.deleteOnShutdown = deleteOnShutdown;
148+
public void setGroupingKey(Map<String, String> groupingKey) {
149+
this.groupingKey = groupingKey;
162150
}
163151

164-
public Map<String, String> getGroupingKeys() {
165-
return this.groupingKeys;
152+
public ShutdownOperation getShutdownOperation() {
153+
return this.shutdownOperation;
166154
}
167155

168-
public void setGroupingKeys(Map<String, String> groupingKeys) {
169-
this.groupingKeys = groupingKeys;
156+
public void setShutdownOperation(ShutdownOperation shutdownOperation) {
157+
this.shutdownOperation = shutdownOperation;
170158
}
171159

172160
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
import io.prometheus.client.CollectorRegistry;
2323
import org.junit.Test;
2424

25-
import org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusMetricsExportAutoConfiguration.PrometheusPushGatewayConfiguration;
2625
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
26+
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager;
2727
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint;
2828
import org.springframework.boot.autoconfigure.AutoConfigurations;
2929
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
@@ -137,8 +137,8 @@ public void withPushGatewayEnabled() {
137137
.withPropertyValues(
138138
"management.metrics.export.prometheus.pushgateway.enabled=true")
139139
.withUserConfiguration(BaseConfiguration.class)
140-
.run((context) -> assertThat(context).hasSingleBean(
141-
PrometheusPushGatewayConfiguration.PushGatewayHandler.class));
140+
.run((context) -> assertThat(context)
141+
.hasSingleBean(PrometheusPushGatewayManager.class));
142142
}
143143

144144
@Configuration

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
34
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
45
<modelVersion>4.0.0</modelVersion>
56
<parent>
@@ -61,6 +62,11 @@
6162
<artifactId>micrometer-registry-prometheus</artifactId>
6263
<optional>true</optional>
6364
</dependency>
65+
<dependency>
66+
<groupId>io.prometheus</groupId>
67+
<artifactId>simpleclient_pushgateway</artifactId>
68+
<optional>true</optional>
69+
</dependency>
6470
<dependency>
6571
<groupId>io.reactivex</groupId>
6672
<artifactId>rxjava-reactive-streams</artifactId>

0 commit comments

Comments
 (0)