Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ newrelic-api = "5.14.0"
# Kotlin 1.7 sample will fail from OkHttp 4.12.0 due to okio dependency being a Kotlin 1.9 module
okhttp = "4.11.0"
postgre = "42.7.2"
prometheus = "1.2.0"
prometheus = "1.2.1"
prometheusSimpleClient = "0.16.0"
reactor = "2022.0.16"
rest-assured = "5.4.0"
Expand Down
9 changes: 4 additions & 5 deletions implementations/micrometer-registry-prometheus/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ dependencies {
api project(':micrometer-core')

api('io.prometheus:prometheus-metrics-core') {
// We only need SpanContext from prometheus-metrics-tracer-common so we should
// exclude(group: 'io.prometheus', module: 'prometheus-metrics-tracer-initializer')
// But right now we cannot since ExemplarSampler imports SpanContextSupplier
exclude(group: 'io.prometheus', module: 'prometheus-metrics-tracer-otel')
exclude(group: 'io.prometheus', module: 'prometheus-metrics-tracer-otel-agent')
// We only need SpanContext from prometheus-metrics-tracer-common, we don't need
// prometheus-metrics-tracer-initializer nor the dependencies it pulls in
exclude(group: 'io.prometheus', module: 'prometheus-metrics-tracer-initializer')
}
api 'io.prometheus:prometheus-metrics-tracer-common'
implementation 'io.prometheus:prometheus-metrics-exposition-formats'

testImplementation 'io.prometheus:prometheus-metrics-tracer-common'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package io.micrometer.prometheusmetrics;

import io.prometheus.metrics.config.ExemplarsProperties;
import io.prometheus.metrics.config.PrometheusProperties;
import io.prometheus.metrics.core.exemplars.ExemplarSampler;
import io.prometheus.metrics.core.exemplars.ExemplarSamplerConfig;
import io.prometheus.metrics.tracer.common.SpanContext;
Expand All @@ -32,30 +31,31 @@
*/
class DefaultExemplarSamplerFactory implements ExemplarSamplerFactory {

private final ExemplarsProperties exemplarProperties = PrometheusProperties.get().getExemplarProperties();
private final ExemplarsProperties exemplarsProperties;

private final ConcurrentMap<Integer, ExemplarSamplerConfig> exemplarSamplerConfigsByNumberOfExemplars = new ConcurrentHashMap<>();

private final ConcurrentMap<double[], ExemplarSamplerConfig> exemplarSamplerConfigsByHistogramUpperBounds = new ConcurrentHashMap<>();

private final SpanContext spanContext;

public DefaultExemplarSamplerFactory(SpanContext spanContext) {
public DefaultExemplarSamplerFactory(SpanContext spanContext, ExemplarsProperties exemplarsProperties) {
this.spanContext = spanContext;
this.exemplarsProperties = exemplarsProperties;
}

@Override
public ExemplarSampler createExemplarSampler(int numberOfExemplars) {
ExemplarSamplerConfig config = exemplarSamplerConfigsByNumberOfExemplars.computeIfAbsent(numberOfExemplars,
key -> new ExemplarSamplerConfig(exemplarProperties, numberOfExemplars));
key -> new ExemplarSamplerConfig(exemplarsProperties, numberOfExemplars));
return new ExemplarSampler(config, spanContext);
}

@Override
public ExemplarSampler createExemplarSampler(double[] histogramClassicUpperBounds) {
ExemplarSamplerConfig config = exemplarSamplerConfigsByHistogramUpperBounds.computeIfAbsent(
histogramClassicUpperBounds,
key -> new ExemplarSamplerConfig(exemplarProperties, histogramClassicUpperBounds));
key -> new ExemplarSamplerConfig(exemplarsProperties, histogramClassicUpperBounds));
return new ExemplarSampler(config, spanContext);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
*/
package io.micrometer.prometheusmetrics;

import io.micrometer.common.lang.Nullable;
import io.micrometer.core.instrument.config.MeterRegistryConfig;
import io.micrometer.core.instrument.config.validate.Validated;

import java.time.Duration;
import java.util.Properties;

import static io.micrometer.core.instrument.config.MeterRegistryConfigValidator.checkAll;
import static io.micrometer.core.instrument.config.MeterRegistryConfigValidator.checkRequired;
Expand Down Expand Up @@ -58,6 +60,19 @@ default Duration step() {
return getDuration(this, "step").orElse(Duration.ofMinutes(1));
}

/**
* @return an instance of {@link Properties} that contains Prometheus Java Client
* config entries, for example
* {@code io.prometheus.exporter.exemplarsOnAllMetricTypes=true}.
* @see https://prometheus.github.io/client_java/config/config/
*/
@Nullable
default Properties prometheusProperties() {
Properties properties = new Properties();
properties.setProperty("io.prometheus.exporter.exemplarsOnAllMetricTypes", "true");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We enable exemplars by default.
If somebody overrides this method, they can do this to reuse the Micrometer defaults but also define their own (see the tests):

@Override
public Properties prometheusProperties() {
    Properties properties = new Properties();
    properties.putAll(PrometheusConfig.super.prometheusProperties());
    properties.setProperty("io.prometheus.exemplars.sampleIntervalMilliseconds", "42");
    return properties;
}

return properties;
}

@Override
default Validated<?> validate() {
return checkAll(this, checkRequired("step", PrometheusConfig::step));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import io.micrometer.core.instrument.internal.DefaultGauge;
import io.micrometer.core.instrument.internal.DefaultMeter;
import io.micrometer.core.instrument.util.TimeUtils;
import io.prometheus.metrics.config.PrometheusProperties;
import io.prometheus.metrics.config.PrometheusPropertiesLoader;
import io.prometheus.metrics.expositionformats.ExpositionFormats;
import io.prometheus.metrics.model.registry.PrometheusRegistry;
Expand Down Expand Up @@ -66,13 +67,12 @@
*/
public class PrometheusMeterRegistry extends MeterRegistry {

private final ExpositionFormats expositionFormats = ExpositionFormats
.init(PrometheusPropertiesLoader.load().getExporterProperties());

private final PrometheusConfig prometheusConfig;

private final PrometheusRegistry registry;

private final ExpositionFormats expositionFormats;

private final ConcurrentMap<String, MicrometerCollector> collectorMap = new ConcurrentHashMap<>();

@Nullable
Expand Down Expand Up @@ -101,7 +101,11 @@ public PrometheusMeterRegistry(PrometheusConfig config, PrometheusRegistry regis

this.prometheusConfig = config;
this.registry = registry;
this.exemplarSamplerFactory = spanContext != null ? new DefaultExemplarSamplerFactory(spanContext) : null;
PrometheusProperties prometheusProperties = config.prometheusProperties() != null
? PrometheusPropertiesLoader.load(config.prometheusProperties()) : PrometheusPropertiesLoader.load();
this.expositionFormats = ExpositionFormats.init(prometheusProperties.getExporterProperties());
this.exemplarSamplerFactory = spanContext != null
? new DefaultExemplarSamplerFactory(spanContext, prometheusProperties.getExemplarProperties()) : null;

config().namingConvention(new PrometheusNamingConvention());
config().onMeterRemoved(this::onMeterRemoved);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import io.micrometer.core.Issue;
import io.micrometer.core.instrument.*;
import io.micrometer.core.instrument.Timer;
import io.micrometer.core.instrument.binder.BaseUnits;
import io.micrometer.core.instrument.binder.jvm.JvmInfoMetrics;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
Expand All @@ -30,10 +31,7 @@
import org.junit.jupiter.api.Test;

import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
Expand Down Expand Up @@ -638,8 +636,23 @@ void openMetricsScrape() {

@Test
void openMetricsScrapeWithExemplars() throws InterruptedException {
PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT, prometheusRegistry,
clock, new TestSpanContex());
PrometheusConfig prometheusConfig = new PrometheusConfig() {
@Override
public String get(String key) {
return null;
}

@Override
public Properties prometheusProperties() {
Properties properties = new Properties();
properties.putAll(PrometheusConfig.super.prometheusProperties());
properties.setProperty("io.prometheus.exemplars.sampleIntervalMilliseconds", "1");
return properties;
}
};

PrometheusMeterRegistry registry = new PrometheusMeterRegistry(prometheusConfig, prometheusRegistry, clock,
new TestSpanContex());

Counter counter = Counter.builder("my.counter").register(registry);
counter.increment();
Expand All @@ -651,18 +664,18 @@ void openMetricsScrapeWithExemplars() throws InterruptedException {

Timer timerWithHistogram = Timer.builder("timer.withHistogram").publishPercentileHistogram().register(registry);
timerWithHistogram.record(15, TimeUnit.MILLISECONDS);
Thread.sleep(100); // sleeping 100ms since the sample interval limit is 90ms
Thread.sleep(5); // sleeping 5ms since the sample interval limit is 1ms
timerWithHistogram.record(150, TimeUnit.MILLISECONDS);
Thread.sleep(100); // sleeping 100ms since the sample interval limit is 90ms
Thread.sleep(5); // sleeping 5ms since the sample interval limit is 1ms
timerWithHistogram.record(60, TimeUnit.SECONDS);

Timer timerWithSlos = Timer.builder("timer.withSlos")
.serviceLevelObjectives(Duration.ofMillis(100), Duration.ofMillis(200), Duration.ofMillis(300))
.register(registry);
timerWithSlos.record(Duration.ofMillis(15));
Thread.sleep(100); // sleeping 100ms since the sample interval limit is 90ms
Thread.sleep(5); // sleeping 5ms since the sample interval limit is 1ms
timerWithSlos.record(Duration.ofMillis(1_500));
Thread.sleep(100); // sleeping 100ms since the sample interval limit is 90ms
Thread.sleep(5); // sleeping 5ms since the sample interval limit is 1ms
timerWithSlos.record(Duration.ofMillis(150));

DistributionSummary summary = DistributionSummary.builder("summary.noHistogram").register(registry);
Expand All @@ -674,18 +687,18 @@ void openMetricsScrapeWithExemplars() throws InterruptedException {
.publishPercentileHistogram()
.register(registry);
summaryWithHistogram.record(0.15);
Thread.sleep(100); // sleeping 100ms since the sample interval limit is 90ms
Thread.sleep(5); // sleeping 5ms since the sample interval limit is 1ms
summaryWithHistogram.record(5E18);
Thread.sleep(100); // sleeping 100ms since the sample interval limit is 90ms
Thread.sleep(5); // sleeping 5ms since the sample interval limit is 1ms
summaryWithHistogram.record(15);

DistributionSummary slos = DistributionSummary.builder("summary.withSlos")
.serviceLevelObjectives(100, 200, 300)
.register(registry);
slos.record(10);
Thread.sleep(100); // sleeping 100ms since the sample interval limit is 90ms
Thread.sleep(5); // sleeping 5ms since the sample interval limit is 1ms
slos.record(1_000);
Thread.sleep(100); // sleeping 100ms since the sample interval limit is 90ms
Thread.sleep(5); // sleeping 5ms since the sample interval limit is 1ms
slos.record(250);

String scraped = registry.scrape("application/openmetrics-text");
Expand Down

This file was deleted.