Merge pull request #30472 from jonatan-ivanov

* gh-30472:
  Add support for Prometheus Exemplars

Closes gh-30472
pull/30641/head
Moritz Halbritter 3 years ago
commit fc2a6554c6

@ -25,11 +25,15 @@ import io.micrometer.core.instrument.Clock;
import io.micrometer.prometheus.PrometheusConfig; import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry; import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.prometheus.client.CollectorRegistry; import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exemplars.DefaultExemplarSampler;
import io.prometheus.client.exemplars.ExemplarSampler;
import io.prometheus.client.exemplars.tracer.common.SpanContextSupplier;
import io.prometheus.client.exporter.BasicAuthHttpConnectionFactory; import io.prometheus.client.exporter.BasicAuthHttpConnectionFactory;
import io.prometheus.client.exporter.PushGateway; import io.prometheus.client.exporter.PushGateway;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
@ -56,6 +60,7 @@ import org.springframework.util.StringUtils;
* *
* @author Jon Schneider * @author Jon Schneider
* @author David J. M. Karlsen * @author David J. M. Karlsen
* @author Jonatan Ivanov
* @since 2.0.0 * @since 2.0.0
*/ */
@AutoConfiguration( @AutoConfiguration(
@ -76,8 +81,9 @@ public class PrometheusMetricsExportAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public PrometheusMeterRegistry prometheusMeterRegistry(PrometheusConfig prometheusConfig, public PrometheusMeterRegistry prometheusMeterRegistry(PrometheusConfig prometheusConfig,
CollectorRegistry collectorRegistry, Clock clock) { CollectorRegistry collectorRegistry, Clock clock, ObjectProvider<ExemplarSampler> exemplarSamplerProvider) {
return new PrometheusMeterRegistry(prometheusConfig, collectorRegistry, clock); return new PrometheusMeterRegistry(prometheusConfig, collectorRegistry, clock,
exemplarSamplerProvider.getIfAvailable());
} }
@Bean @Bean
@ -86,6 +92,13 @@ public class PrometheusMetricsExportAutoConfiguration {
return new CollectorRegistry(true); return new CollectorRegistry(true);
} }
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(SpanContextSupplier.class)
public ExemplarSampler exemplarSampler(SpanContextSupplier spanContextSupplier) {
return new DefaultExemplarSampler(spanContextSupplier);
}
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnAvailableEndpoint(endpoint = PrometheusScrapeEndpoint.class) @ConditionalOnAvailableEndpoint(endpoint = PrometheusScrapeEndpoint.class)
public static class PrometheusScrapeEndpointConfiguration { public static class PrometheusScrapeEndpointConfiguration {

@ -22,6 +22,8 @@ import io.micrometer.core.instrument.Clock;
import io.micrometer.prometheus.PrometheusConfig; import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry; import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.prometheus.client.CollectorRegistry; import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exemplars.ExemplarSampler;
import io.prometheus.client.exemplars.tracer.common.SpanContextSupplier;
import io.prometheus.client.exporter.BasicAuthHttpConnectionFactory; import io.prometheus.client.exporter.BasicAuthHttpConnectionFactory;
import io.prometheus.client.exporter.DefaultHttpConnectionFactory; import io.prometheus.client.exporter.DefaultHttpConnectionFactory;
import io.prometheus.client.exporter.HttpConnectionFactory; import io.prometheus.client.exporter.HttpConnectionFactory;
@ -50,6 +52,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* *
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Jonatan Ivanov
*/ */
@ExtendWith(OutputCaptureExtension.class) @ExtendWith(OutputCaptureExtension.class)
class PrometheusMetricsExportAutoConfigurationTests { class PrometheusMetricsExportAutoConfigurationTests {
@ -109,6 +112,20 @@ class PrometheusMetricsExportAutoConfigurationTests {
.hasSingleBean(PrometheusConfig.class)); .hasSingleBean(PrometheusConfig.class));
} }
@Test
void autoConfiguresExemplarSamplerIfSpanContextSupplierIsPresent() {
this.contextRunner.withUserConfiguration(ExemplarsConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(SpanContextSupplier.class)
.hasSingleBean(ExemplarSampler.class).hasSingleBean(PrometheusMeterRegistry.class));
}
@Test
void exemplarSamplerIsNotAutoConfiguredIfSpanContextSupplierIsMissing() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean(SpanContextSupplier.class)
.doesNotHaveBean(ExemplarSampler.class).hasSingleBean(PrometheusMeterRegistry.class));
}
@Test @Test
void addsScrapeEndpointToManagementContext() { void addsScrapeEndpointToManagementContext() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class)) this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
@ -271,4 +288,25 @@ class PrometheusMetricsExportAutoConfigurationTests {
} }
@Configuration(proxyBeanMethods = false)
@Import(BaseConfiguration.class)
static class ExemplarsConfiguration {
@Bean
SpanContextSupplier spanContextSupplier() {
return new SpanContextSupplier() {
@Override
public String getTraceId() {
return null;
}
@Override
public String getSpanId() {
return null;
}
};
}
}
} }

@ -455,6 +455,9 @@ The following example `scrape_config` adds to `prometheus.yml`:
- targets: ["HOST:PORT"] - targets: ["HOST:PORT"]
---- ----
https://prometheus.io/docs/prometheus/latest/feature_flags/#exemplars-storage[Prometheus Exemplars] are also supported. To enable this feature, a `SpanContextSupplier` bean should present. If you use https://spring.io/projects/spring-cloud-sleuth[Spring Cloud Sleuth], this will be auto-configured for you, but you can always create your own if you want. +
Please check the https://prometheus.io/docs/prometheus/latest/feature_flags/#exemplars-storage[Prometheus Docs], since this feature needs to be explicitly enabled on Prometheus' side, and it is only supported using the https://github.com/OpenObservability/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#exemplars[OpenMetrics] format.
For ephemeral or batch jobs that may not exist long enough to be scraped, you can use https://github.com/prometheus/pushgateway[Prometheus Pushgateway] support to expose the metrics to Prometheus. For ephemeral or batch jobs that may not exist long enough to be scraped, you can use https://github.com/prometheus/pushgateway[Prometheus Pushgateway] support to expose the metrics to Prometheus.
To enable Prometheus Pushgateway support, add the following dependency to your project: To enable Prometheus Pushgateway support, add the following dependency to your project:

Loading…
Cancel
Save