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
This commit is contained in:
parent
4e71981f77
commit
20ecf73cd1
|
|
@ -16,25 +16,21 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
|
||||
import io.micrometer.core.instrument.Clock;
|
||||
import io.micrometer.prometheus.PrometheusConfig;
|
||||
import io.micrometer.prometheus.PrometheusMeterRegistry;
|
||||
import io.prometheus.client.CollectorRegistry;
|
||||
import io.prometheus.client.exporter.PushGateway;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
|
||||
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager;
|
||||
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager.ShutdownOperation;
|
||||
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
|
|
@ -53,6 +49,7 @@ import org.springframework.core.env.Environment;
|
|||
*
|
||||
* @since 2.0.0
|
||||
* @author Jon Schneider
|
||||
* @author David J. M. Karlsen
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfigureBefore({ CompositeMeterRegistryAutoConfiguration.class,
|
||||
|
|
@ -100,108 +97,42 @@ public class PrometheusMetricsExportAutoConfiguration {
|
|||
/**
|
||||
* Configuration for <a href="https://github.com/prometheus/pushgateway">Prometheus
|
||||
* Pushgateway</a>.
|
||||
*
|
||||
* @author David J. M. Karlsen
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(PushGateway.class)
|
||||
@ConditionalOnProperty(prefix = "management.metrics.export.prometheus.pushgateway", name = "enabled")
|
||||
public static class PrometheusPushGatewayConfiguration {
|
||||
|
||||
/**
|
||||
* The fallback job name. We use 'spring' since there's a history of Prometheus
|
||||
* spring integration defaulting to that name from when Prometheus integration
|
||||
* didn't exist in Spring itself.
|
||||
*/
|
||||
private static final String FALLBACK_JOB = "spring";
|
||||
|
||||
@Bean
|
||||
public PushGatewayHandler pushGatewayHandler(CollectorRegistry collectorRegistry,
|
||||
@ConditionalOnMissingBean
|
||||
public PrometheusPushGatewayManager prometheusPushGatewayManager(
|
||||
CollectorRegistry collectorRegistry,
|
||||
PrometheusProperties prometheusProperties, Environment environment) {
|
||||
return new PushGatewayHandler(collectorRegistry, prometheusProperties,
|
||||
environment);
|
||||
PrometheusProperties.Pushgateway properties = prometheusProperties
|
||||
.getPushgateway();
|
||||
PushGateway pushGateway = new PushGateway(properties.getBaseUrl());
|
||||
Duration pushRate = properties.getPushRate();
|
||||
String job = getJob(properties, environment);
|
||||
Map<String, String> groupingKey = properties.getGroupingKey();
|
||||
ShutdownOperation shutdownOperation = properties.getShutdownOperation();
|
||||
return new PrometheusPushGatewayManager(pushGateway, collectorRegistry,
|
||||
pushRate, job, groupingKey, shutdownOperation);
|
||||
}
|
||||
|
||||
static class PushGatewayHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory
|
||||
.getLogger(PrometheusPushGatewayConfiguration.class);
|
||||
|
||||
private final CollectorRegistry collectorRegistry;
|
||||
|
||||
private final PrometheusProperties.PushgatewayProperties pushgatewayProperties;
|
||||
|
||||
private final PushGateway pushGateway;
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
private final ScheduledExecutorService executorService;
|
||||
|
||||
PushGatewayHandler(CollectorRegistry collectorRegistry,
|
||||
PrometheusProperties prometheusProperties, Environment environment) {
|
||||
this.collectorRegistry = collectorRegistry;
|
||||
this.pushgatewayProperties = prometheusProperties.getPushgateway();
|
||||
this.pushGateway = new PushGateway(
|
||||
this.pushgatewayProperties.getBaseUrl());
|
||||
this.environment = environment;
|
||||
this.executorService = Executors.newSingleThreadScheduledExecutor((r) -> {
|
||||
Thread thread = new Thread(r);
|
||||
thread.setDaemon(true);
|
||||
thread.setName("micrometer-pushgateway");
|
||||
return thread;
|
||||
});
|
||||
this.executorService.scheduleAtFixedRate(this::push, 0,
|
||||
this.pushgatewayProperties.getPushRate().toMillis(),
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
void push() {
|
||||
try {
|
||||
this.pushGateway.pushAdd(this.collectorRegistry, getJobName(),
|
||||
this.pushgatewayProperties.getGroupingKeys());
|
||||
}
|
||||
catch (UnknownHostException ex) {
|
||||
this.logger.error("Unable to locate host '"
|
||||
+ this.pushgatewayProperties.getBaseUrl()
|
||||
+ "'. No longer attempting metrics publication to this host");
|
||||
this.executorService.shutdown();
|
||||
}
|
||||
catch (Throwable throwable) {
|
||||
this.logger.error("Unable to push metrics to Prometheus Pushgateway",
|
||||
throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
void shutdown() {
|
||||
this.executorService.shutdown();
|
||||
if (this.pushgatewayProperties.isPushOnShutdown()) {
|
||||
push();
|
||||
}
|
||||
if (this.pushgatewayProperties.isDeleteOnShutdown()) {
|
||||
delete();
|
||||
}
|
||||
}
|
||||
|
||||
private void delete() {
|
||||
try {
|
||||
this.pushGateway.delete(getJobName(),
|
||||
this.pushgatewayProperties.getGroupingKeys());
|
||||
}
|
||||
catch (Throwable throwable) {
|
||||
this.logger.error(
|
||||
"Unable to delete metrics from Prometheus Pushgateway",
|
||||
throwable);
|
||||
}
|
||||
}
|
||||
|
||||
private String getJobName() {
|
||||
String job = this.pushgatewayProperties.getJob();
|
||||
if (job == null) {
|
||||
job = this.environment.getProperty("spring.application.name");
|
||||
}
|
||||
if (job == null) {
|
||||
// There's a history of Prometheus spring integration defaulting the
|
||||
// getJobName name to "spring" from when
|
||||
// Prometheus integration didn't exist in Spring itself.
|
||||
job = "spring";
|
||||
}
|
||||
return job;
|
||||
}
|
||||
|
||||
private String getJob(PrometheusProperties.Pushgateway properties,
|
||||
Environment environment) {
|
||||
String job = properties.getJob();
|
||||
job = (job != null) ? job
|
||||
: environment.getProperty("spring.application.name");
|
||||
job = (job != null) ? job : FALLBACK_JOB;
|
||||
return job;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import java.time.Duration;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager.ShutdownOperation;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
|
|
@ -42,7 +43,7 @@ public class PrometheusProperties {
|
|||
* Configuration options for using Prometheus Pushgateway, allowing metrics to be
|
||||
* pushed when they cannot be scraped.
|
||||
*/
|
||||
private PushgatewayProperties pushgateway = new PushgatewayProperties();
|
||||
private Pushgateway pushgateway = new Pushgateway();
|
||||
|
||||
/**
|
||||
* Step size (i.e. reporting frequency) to use.
|
||||
|
|
@ -65,18 +66,18 @@ public class PrometheusProperties {
|
|||
this.step = step;
|
||||
}
|
||||
|
||||
public PushgatewayProperties getPushgateway() {
|
||||
public Pushgateway getPushgateway() {
|
||||
return this.pushgateway;
|
||||
}
|
||||
|
||||
public void setPushgateway(PushgatewayProperties pushgateway) {
|
||||
public void setPushgateway(Pushgateway pushgateway) {
|
||||
this.pushgateway = pushgateway;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for push-based interaction with Prometheus.
|
||||
*/
|
||||
public static class PushgatewayProperties {
|
||||
public static class Pushgateway {
|
||||
|
||||
/**
|
||||
* Enable publishing via a Prometheus Pushgateway.
|
||||
|
|
@ -84,34 +85,29 @@ public class PrometheusProperties {
|
|||
private Boolean enabled = false;
|
||||
|
||||
/**
|
||||
* Required host:port or ip:port of the Pushgateway.
|
||||
* Base URL for the Pushgateway.
|
||||
*/
|
||||
private String baseUrl = "localhost:9091";
|
||||
|
||||
/**
|
||||
* Required identifier for this application instance.
|
||||
*/
|
||||
private String job;
|
||||
|
||||
/**
|
||||
* Frequency with which to push metrics to Pushgateway.
|
||||
* Frequency with which to push metrics.
|
||||
*/
|
||||
private Duration pushRate = Duration.ofMinutes(1);
|
||||
|
||||
/**
|
||||
* Push metrics right before shut-down. Mostly useful for batch jobs.
|
||||
* Job identifier for this application instance.
|
||||
*/
|
||||
private boolean pushOnShutdown = true;
|
||||
private String job;
|
||||
|
||||
/**
|
||||
* Delete metrics from Pushgateway when application is shut-down.
|
||||
* Grouping key for the pushed metrics.
|
||||
*/
|
||||
private boolean deleteOnShutdown = true;
|
||||
private Map<String, String> groupingKey = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Used to group metrics in pushgateway. A common example is setting
|
||||
* Operation that should be performed on shutdown.
|
||||
*/
|
||||
private Map<String, String> groupingKeys = new HashMap<>();
|
||||
private ShutdownOperation shutdownOperation = ShutdownOperation.NONE;
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return this.enabled;
|
||||
|
|
@ -129,14 +125,6 @@ public class PrometheusProperties {
|
|||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public String getJob() {
|
||||
return this.job;
|
||||
}
|
||||
|
||||
public void setJob(String job) {
|
||||
this.job = job;
|
||||
}
|
||||
|
||||
public Duration getPushRate() {
|
||||
return this.pushRate;
|
||||
}
|
||||
|
|
@ -145,28 +133,28 @@ public class PrometheusProperties {
|
|||
this.pushRate = pushRate;
|
||||
}
|
||||
|
||||
public boolean isPushOnShutdown() {
|
||||
return this.pushOnShutdown;
|
||||
public String getJob() {
|
||||
return this.job;
|
||||
}
|
||||
|
||||
public void setPushOnShutdown(boolean pushOnShutdown) {
|
||||
this.pushOnShutdown = pushOnShutdown;
|
||||
public void setJob(String job) {
|
||||
this.job = job;
|
||||
}
|
||||
|
||||
public boolean isDeleteOnShutdown() {
|
||||
return this.deleteOnShutdown;
|
||||
public Map<String, String> getGroupingKey() {
|
||||
return this.groupingKey;
|
||||
}
|
||||
|
||||
public void setDeleteOnShutdown(boolean deleteOnShutdown) {
|
||||
this.deleteOnShutdown = deleteOnShutdown;
|
||||
public void setGroupingKey(Map<String, String> groupingKey) {
|
||||
this.groupingKey = groupingKey;
|
||||
}
|
||||
|
||||
public Map<String, String> getGroupingKeys() {
|
||||
return this.groupingKeys;
|
||||
public ShutdownOperation getShutdownOperation() {
|
||||
return this.shutdownOperation;
|
||||
}
|
||||
|
||||
public void setGroupingKeys(Map<String, String> groupingKeys) {
|
||||
this.groupingKeys = groupingKeys;
|
||||
public void setShutdownOperation(ShutdownOperation shutdownOperation) {
|
||||
this.shutdownOperation = shutdownOperation;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import io.micrometer.prometheus.PrometheusMeterRegistry;
|
|||
import io.prometheus.client.CollectorRegistry;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusMetricsExportAutoConfiguration.PrometheusPushGatewayConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
|
||||
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager;
|
||||
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
|
|
@ -137,8 +137,8 @@ public class PrometheusMetricsExportAutoConfigurationTests {
|
|||
.withPropertyValues(
|
||||
"management.metrics.export.prometheus.pushgateway.enabled=true")
|
||||
.withUserConfiguration(BaseConfiguration.class)
|
||||
.run((context) -> assertThat(context).hasSingleBean(
|
||||
PrometheusPushGatewayConfiguration.PushGatewayHandler.class));
|
||||
.run((context) -> assertThat(context)
|
||||
.hasSingleBean(PrometheusPushGatewayManager.class));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
|
|
@ -61,6 +62,11 @@
|
|||
<artifactId>micrometer-registry-prometheus</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.prometheus</groupId>
|
||||
<artifactId>simpleclient_pushgateway</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxjava-reactive-streams</artifactId>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* Copyright 2012-2018 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.metrics.export.prometheus;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
import io.prometheus.client.CollectorRegistry;
|
||||
import io.prometheus.client.exporter.PushGateway;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Class that can be used to managed the pushing of metrics to a {@link PushGateway
|
||||
* Prometheus PushGateway}. Handles the scheduling of push operations, error handling and
|
||||
* shutdown operations.
|
||||
*
|
||||
* @author David J. M. Karlsen
|
||||
* @author Phillip Webb
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public class PrometheusPushGatewayManager {
|
||||
|
||||
private static final Logger logger = LoggerFactory
|
||||
.getLogger(PrometheusPushGatewayManager.class);
|
||||
|
||||
private final PushGateway pushGateway;
|
||||
|
||||
private final CollectorRegistry registry;
|
||||
|
||||
private final String job;
|
||||
|
||||
private final Map<String, String> groupingKey;
|
||||
|
||||
private final ShutdownOperation shutdownOperation;
|
||||
|
||||
private final TaskScheduler scheduler;
|
||||
|
||||
private ScheduledFuture<?> scheduled;
|
||||
|
||||
/**
|
||||
* Create a new {@link PrometheusPushGatewayManager} instance using a single threaded
|
||||
* {@link TaskScheduler}.
|
||||
* @param pushGateway the source push gateway
|
||||
* @param registry the collector registry to push
|
||||
* @param pushRate the rate at which push operations occur
|
||||
* @param job the job ID for the operation
|
||||
* @param groupingKeys an optional set of grouping keys for the operation
|
||||
* @param shutdownOperation the shutdown operation that should be performed when
|
||||
* context is closed.
|
||||
*/
|
||||
public PrometheusPushGatewayManager(PushGateway pushGateway,
|
||||
CollectorRegistry registry, Duration pushRate, String job,
|
||||
Map<String, String> groupingKeys, ShutdownOperation shutdownOperation) {
|
||||
this(pushGateway, registry, new PushGatewayTaskScheduler(), pushRate, job,
|
||||
groupingKeys, shutdownOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link PrometheusPushGatewayManager} instance.
|
||||
* @param pushGateway the source push gateway
|
||||
* @param registry the collector registry to push
|
||||
* @param scheduler the scheduler used for operations
|
||||
* @param pushRate the rate at which push operations occur
|
||||
* @param job the job ID for the operation
|
||||
* @param groupingKey an optional set of grouping keys for the operation
|
||||
* @param shutdownOperation the shutdown operation that should be performed when
|
||||
* context is closed.
|
||||
*/
|
||||
public PrometheusPushGatewayManager(PushGateway pushGateway,
|
||||
CollectorRegistry registry, TaskScheduler scheduler, Duration pushRate,
|
||||
String job, Map<String, String> groupingKey,
|
||||
ShutdownOperation shutdownOperation) {
|
||||
Assert.notNull(pushGateway, "PushGateway must not be null");
|
||||
Assert.notNull(registry, "Registry must not be null");
|
||||
Assert.notNull(scheduler, "Scheduler must not be null");
|
||||
Assert.notNull(pushRate, "PushRate must not be null");
|
||||
Assert.hasLength(job, "Job must not be empty");
|
||||
this.pushGateway = pushGateway;
|
||||
this.registry = registry;
|
||||
this.job = job;
|
||||
this.groupingKey = groupingKey;
|
||||
this.shutdownOperation = (shutdownOperation != null) ? shutdownOperation
|
||||
: ShutdownOperation.NONE;
|
||||
this.scheduler = scheduler;
|
||||
this.scheduled = this.scheduler.scheduleAtFixedRate(this::push, pushRate);
|
||||
}
|
||||
|
||||
private void push() {
|
||||
try {
|
||||
this.pushGateway.pushAdd(this.registry, this.job, this.groupingKey);
|
||||
}
|
||||
catch (UnknownHostException ex) {
|
||||
String host = ex.getMessage();
|
||||
String message = "Unable to locate prometheus push gateway host";
|
||||
message += StringUtils.hasLength(host) ? " '" + host + "'" : "";
|
||||
message += ". No longer attempting metrics publication to this host";
|
||||
logger.error(message, ex);
|
||||
shutdown(ShutdownOperation.NONE);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.error("Unable to push metrics to Prometheus Pushgateway", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void delete() {
|
||||
try {
|
||||
this.pushGateway.delete(this.job, this.groupingKey);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.error("Unable to delete metrics from Prometheus Pushgateway", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the manager, running any {@link ShutdownOperation}.
|
||||
*/
|
||||
public void shutdown() {
|
||||
shutdown(this.shutdownOperation);
|
||||
}
|
||||
|
||||
private void shutdown(ShutdownOperation shutdownOperation) {
|
||||
if (this.scheduler instanceof PushGatewayTaskScheduler) {
|
||||
((PushGatewayTaskScheduler) this.scheduler).shutdown();
|
||||
}
|
||||
this.scheduled.cancel(false);
|
||||
switch (shutdownOperation) {
|
||||
case PUSH:
|
||||
push();
|
||||
break;
|
||||
case DELETE:
|
||||
delete();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operation that should be performed on shutdown.
|
||||
*/
|
||||
public enum ShutdownOperation {
|
||||
|
||||
/**
|
||||
* Don't perform any shutdown operation.
|
||||
*/
|
||||
NONE,
|
||||
|
||||
/**
|
||||
* Perform a 'push' before shutdown.
|
||||
*/
|
||||
PUSH,
|
||||
|
||||
/**
|
||||
* Perform a 'delete' before shutdown.
|
||||
*/
|
||||
DELETE
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link TaskScheduler} used when the user doesn't specify one.
|
||||
*/
|
||||
static class PushGatewayTaskScheduler extends ThreadPoolTaskScheduler {
|
||||
|
||||
PushGatewayTaskScheduler() {
|
||||
setPoolSize(1);
|
||||
setDaemon(true);
|
||||
setThreadGroupName("prometheus-push-gateway");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledExecutorService getScheduledExecutor()
|
||||
throws IllegalStateException {
|
||||
return Executors.newSingleThreadScheduledExecutor(this::newThread);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* Copyright 2012-2018 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.metrics.export.prometheus;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
import io.prometheus.client.CollectorRegistry;
|
||||
import io.prometheus.client.exporter.PushGateway;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager.PushGatewayTaskScheduler;
|
||||
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager.ShutdownOperation;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isA;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.willThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link PrometheusPushGatewayManager}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class PrometheusPushGatewayManagerTests {
|
||||
|
||||
@Mock
|
||||
private PushGateway pushGateway;
|
||||
|
||||
@Mock
|
||||
private CollectorRegistry registry;
|
||||
|
||||
private TaskScheduler scheduler;
|
||||
|
||||
private Duration pushRate = Duration.ofSeconds(1);
|
||||
|
||||
private Map<String, String> groupingKey = Collections.singletonMap("foo", "bar");
|
||||
|
||||
@Captor
|
||||
private ArgumentCaptor<Runnable> task;
|
||||
|
||||
@Mock
|
||||
private ScheduledFuture<Object> future;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.scheduler = mockScheduler(TaskScheduler.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenPushGatewayIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new PrometheusPushGatewayManager(null, this.registry,
|
||||
this.scheduler, this.pushRate, "job", this.groupingKey, null))
|
||||
.withMessage("PushGateway must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenCollectorRegistryIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new PrometheusPushGatewayManager(this.pushGateway, null,
|
||||
this.scheduler, this.pushRate, "job", this.groupingKey, null))
|
||||
.withMessage("Registry must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenSchedulerIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> new PrometheusPushGatewayManager(this.pushGateway, this.registry,
|
||||
null, this.pushRate, "job", this.groupingKey, null))
|
||||
.withMessage("Scheduler must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenPushRateIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> new PrometheusPushGatewayManager(this.pushGateway, this.registry,
|
||||
this.scheduler, null, "job", this.groupingKey, null))
|
||||
.withMessage("PushRate must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenJobIsEmptyThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> new PrometheusPushGatewayManager(this.pushGateway, this.registry,
|
||||
this.scheduler, this.pushRate, "", this.groupingKey, null))
|
||||
.withMessage("Job must not be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createShouldSchedulePushAsFixedRate() throws Exception {
|
||||
new PrometheusPushGatewayManager(this.pushGateway, this.registry, this.scheduler,
|
||||
this.pushRate, "job", this.groupingKey, null);
|
||||
verify(this.scheduler).scheduleAtFixedRate(this.task.capture(),
|
||||
eq(this.pushRate));
|
||||
this.task.getValue().run();
|
||||
verify(this.pushGateway).pushAdd(this.registry, "job", this.groupingKey);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shutdownWhenOwnsSchedulerDoesShutdownScheduler() {
|
||||
PushGatewayTaskScheduler ownedScheduler = mockScheduler(
|
||||
PushGatewayTaskScheduler.class);
|
||||
PrometheusPushGatewayManager manager = new PrometheusPushGatewayManager(
|
||||
this.pushGateway, this.registry, ownedScheduler, this.pushRate, "job",
|
||||
this.groupingKey, null);
|
||||
manager.shutdown();
|
||||
verify(ownedScheduler).shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shutdownWhenDoesNotOwnSchedulerDoesNotShutdownScheduler() {
|
||||
ThreadPoolTaskScheduler otherScheduler = mockScheduler(
|
||||
ThreadPoolTaskScheduler.class);
|
||||
PrometheusPushGatewayManager manager = new PrometheusPushGatewayManager(
|
||||
this.pushGateway, this.registry, otherScheduler, this.pushRate, "job",
|
||||
this.groupingKey, null);
|
||||
manager.shutdown();
|
||||
verify(otherScheduler, never()).shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shutdownWhenShutdownOperationIsPushPerformsPushOnShutdown()
|
||||
throws Exception {
|
||||
PrometheusPushGatewayManager manager = new PrometheusPushGatewayManager(
|
||||
this.pushGateway, this.registry, this.scheduler, this.pushRate, "job",
|
||||
this.groupingKey, ShutdownOperation.PUSH);
|
||||
manager.shutdown();
|
||||
verify(this.future).cancel(false);
|
||||
verify(this.pushGateway).pushAdd(this.registry, "job", this.groupingKey);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shutdownWhenShutdownOperationIsDeletePerformsDeleteOnShutdown()
|
||||
throws Exception {
|
||||
PrometheusPushGatewayManager manager = new PrometheusPushGatewayManager(
|
||||
this.pushGateway, this.registry, this.scheduler, this.pushRate, "job",
|
||||
this.groupingKey, ShutdownOperation.DELETE);
|
||||
manager.shutdown();
|
||||
verify(this.future).cancel(false);
|
||||
verify(this.pushGateway).delete("job", this.groupingKey);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shutdownWhenShutdownOperationIsNoneDoesNothing() {
|
||||
PrometheusPushGatewayManager manager = new PrometheusPushGatewayManager(
|
||||
this.pushGateway, this.registry, this.scheduler, this.pushRate, "job",
|
||||
this.groupingKey, ShutdownOperation.NONE);
|
||||
manager.shutdown();
|
||||
verify(this.future).cancel(false);
|
||||
verifyZeroInteractions(this.pushGateway);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pushWhenUnknownHostExceptionIsThrownDoesShutdown() throws Exception {
|
||||
new PrometheusPushGatewayManager(this.pushGateway, this.registry, this.scheduler,
|
||||
this.pushRate, "job", this.groupingKey, null);
|
||||
verify(this.scheduler).scheduleAtFixedRate(this.task.capture(),
|
||||
eq(this.pushRate));
|
||||
willThrow(new UnknownHostException("foo")).given(this.pushGateway)
|
||||
.pushAdd(this.registry, "job", this.groupingKey);
|
||||
this.task.getValue().run();
|
||||
verify(this.future).cancel(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pushDoesNotThrowException() throws Exception {
|
||||
new PrometheusPushGatewayManager(this.pushGateway, this.registry, this.scheduler,
|
||||
this.pushRate, "job", this.groupingKey, null);
|
||||
verify(this.scheduler).scheduleAtFixedRate(this.task.capture(),
|
||||
eq(this.pushRate));
|
||||
willThrow(RuntimeException.class).given(this.pushGateway).pushAdd(this.registry,
|
||||
"job", this.groupingKey);
|
||||
this.task.getValue().run();
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private <T extends TaskScheduler> T mockScheduler(Class<T> type) {
|
||||
T scheduler = mock(type);
|
||||
given(scheduler.scheduleAtFixedRate(isA(Runnable.class), isA(Duration.class)))
|
||||
.willReturn((ScheduledFuture) this.future);
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -143,7 +143,6 @@
|
|||
<nio-multipart-parser.version>1.1.0</nio-multipart-parser.version>
|
||||
<pooled-jms-version>1.0.3</pooled-jms-version>
|
||||
<postgresql.version>42.2.5</postgresql.version>
|
||||
<!-- need to take care that this version harmonizes with micrometer ones -->
|
||||
<prometheus-pushgateway.version>0.5.0</prometheus-pushgateway.version>
|
||||
<quartz.version>2.3.0</quartz.version>
|
||||
<querydsl.version>4.2.1</querydsl.version>
|
||||
|
|
|
|||
|
|
@ -1480,6 +1480,12 @@ content into your application. Rather, pick only the properties that you need.
|
|||
management.metrics.export.prometheus.descriptions=true # Whether to enable publishing descriptions as part of the scrape payload to Prometheus. Turn this off to minimize the amount of data sent on each scrape.
|
||||
management.metrics.export.prometheus.enabled=true # Whether exporting of metrics to Prometheus is enabled.
|
||||
management.metrics.export.prometheus.step=1m # Step size (i.e. reporting frequency) to use.
|
||||
management.metrics.export.prometheus.pushgateway.base-url=localhost:9091 # Base URL for the Pushgateway.
|
||||
management.metrics.export.prometheus.pushgateway.enabled=false # Enable publishing via a Prometheus Pushgateway.
|
||||
management.metrics.export.prometheus.pushgateway.grouping-key= # Grouping key for the pushed metrics.
|
||||
management.metrics.export.prometheus.pushgateway.job= # Job identifier for this application instance.
|
||||
management.metrics.export.prometheus.pushgateway.push-rate=1m # Frequency with which to push metrics.
|
||||
management.metrics.export.prometheus.pushgateway.shutdown-operation= # Operation that should be performed on shutdown (none, push, delete).
|
||||
management.metrics.export.signalfx.access-token= # SignalFX access token.
|
||||
management.metrics.export.signalfx.batch-size=10000 # Number of measurements per request to use for this backend. If more measurements are found, then multiple requests will be made.
|
||||
management.metrics.export.signalfx.connect-timeout=1s # Connection timeout for requests to this backend.
|
||||
|
|
|
|||
Loading…
Reference in New Issue