Another variant of solving the problem is overriding the ExemplarSampler
bean provided by Spring Boot auto-configuration with an implementation that disables exemplar
s only for counter
metrics (histogram
metrics will still have exemplar
s attached).
There are 2 simple ways of doing this.
You can define ExemplarSampler
bean, that will substitue the default DefaultExemplarSampler
implementation. The implementation could just delegate calls to all methods except io.prometheus.client.exemplars.CounterExemplarSampler.sample
to the DefaultExemplarSampler
instance, and CounterExemplarSampler.sample
should just return null
.
You can define a BeanPostProcessor
, that does basically the same, but does not rely on the DefaultExemplarSampler
implementation. It will just wrap any found ExemplarSampler
bean into the new implementation.
An example of such BeanPostProcessor
:
/**
* A {@link BeanPostProcessor} that wraps any {@link ExemplarSampler} bean with an implementation
* that delegates all method calls to the wrapped bean, except
* {@link io.prometheus.client.exemplars.CounterExemplarSampler#sample(double, Exemplar)}.
* This have an effect of disabled Exemplars for counter metrics, introduced in Prometheus 2.43.
* Any counter metrics with exemplars are ignored by any older Prometheus version on scrapping,
* so it is important for counter metrics to <b>not</b> have exemplars, or they will be ignored,
* if scrapped by Prometheus version less than 2.43.
*
* @see <a href="https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.2-Release-Notes#broader-exemplar-support-in-micrometer-112">Spring Boot 3.2 Release Notes</a>
* @see <a href="https://github.com/GoogleCloudPlatform/prometheus-engine/issues/812">Managed Prometheus on GCP Issue</a>
* @see <a href="https://mcmap.net/q/1284966/-disable-exemplars-support-in-spring-boot-3-2-to-avoid-prometheus-problem-with-scrapping-metrics">Stackoverflow discussion</a>
* @see <a href="https://github.com/micrometer-metrics/micrometer/pull/3996">Micrometer Metrics PR</a>
*/
@Component
public class ExemplarSamplerBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ExemplarSampler delegate) {
return new IgnoreCountersExemplarSampler(delegate);
}
return bean;
}
@RequiredArgsConstructor
static class IgnoreCountersExemplarSampler implements ExemplarSampler {
private final ExemplarSampler delegate;
@Override
public Exemplar sample(double increment, Exemplar previous) {
// Do not return exemplar for counter metrics to allow them to be scrapped by Prometheus 2.42 and below
return null;
}
@Override
public Exemplar sample(double value, double bucketFrom, double bucketTo, Exemplar previous) {
return delegate.sample(value, bucketFrom, bucketTo, previous);
}
}
}
Put this component into a component-scanned package and that's all. Counter metrics will not include exemplars anymore and Prometheus older than 2.43 will successfully scrap them.
If you do not want to use BeanPostProcessor
, here is the solution with @Bean
override:
@Configuration
@ConditionalOnEnabledMetricsExport("prometheus")
public class PrometheusExemplarsConfig {
@Bean
IgnoreCountersExemplarSampler exemplarSampler(SpanContextSupplier spanContextSupplier) {
ExemplarSampler delegate = new DefaultExemplarSampler(spanContextSupplier);
return new IgnoreCountersExemplarSampler(delegate);
}
}
The implementation of IgnoreCountersExemplarSampler
is the same.