From 46fdadc2a409477efbb0ec2e2594e6561991667b Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 27 Jun 2024 16:29:18 +0530 Subject: [PATCH] adds config to enable default exponential histogram --- .../prometheus/PrometheusHttpServer.java | 12 ++++++++- .../PrometheusHttpServerBuilder.java | 21 +++++++++++++++- .../PrometheusMetricReaderProvider.java | 21 ++++++++++++++++ .../prometheus/PrometheusHttpServerTest.java | 3 +++ .../PrometheusMetricReaderProviderTest.java | 25 +++++++++++++++++++ 5 files changed, 80 insertions(+), 2 deletions(-) diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServer.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServer.java index 0a306fccaa3..78609ee7549 100644 --- a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServer.java +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServer.java @@ -14,9 +14,11 @@ import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.internal.DaemonThreadFactory; +import io.opentelemetry.sdk.metrics.Aggregation; import io.opentelemetry.sdk.metrics.InstrumentType; import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import io.opentelemetry.sdk.metrics.export.CollectionRegistration; +import io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector; import io.opentelemetry.sdk.metrics.export.MetricReader; import io.prometheus.metrics.exporter.httpserver.HTTPServer; import io.prometheus.metrics.model.registry.PrometheusRegistry; @@ -41,6 +43,7 @@ public final class PrometheusHttpServer implements MetricReader { private final PrometheusRegistry prometheusRegistry; private final String host; private final MemoryMode memoryMode; + private final DefaultAggregationSelector defaultAggregationSelector; /** * Returns a new {@link PrometheusHttpServer} which can be registered to an {@link @@ -65,7 +68,8 @@ public static PrometheusHttpServerBuilder builder() { boolean otelScopeEnabled, @Nullable Predicate allowedResourceAttributesFilter, MemoryMode memoryMode, - @Nullable HttpHandler defaultHandler) { + @Nullable HttpHandler defaultHandler, + DefaultAggregationSelector defaultAggregationSelector) { this.builder = builder; this.prometheusMetricReader = new PrometheusMetricReader(otelScopeEnabled, allowedResourceAttributesFilter); @@ -92,6 +96,7 @@ public static PrometheusHttpServerBuilder builder() { } catch (IOException e) { throw new UncheckedIOException("Could not create Prometheus HTTP server", e); } + this.defaultAggregationSelector = defaultAggregationSelector; } @Override @@ -99,6 +104,11 @@ public AggregationTemporality getAggregationTemporality(InstrumentType instrumen return prometheusMetricReader.getAggregationTemporality(instrumentType); } + @Override + public Aggregation getDefaultAggregation(InstrumentType instrumentType) { + return defaultAggregationSelector.getDefaultAggregation(instrumentType); + } + @Override public MemoryMode getMemoryMode() { return memoryMode; diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerBuilder.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerBuilder.java index 20f36fdf426..89bae976f21 100644 --- a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerBuilder.java +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerBuilder.java @@ -10,6 +10,9 @@ import com.sun.net.httpserver.HttpHandler; import io.opentelemetry.sdk.common.export.MemoryMode; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector; +import io.opentelemetry.sdk.metrics.export.MetricExporter; import io.prometheus.metrics.model.registry.PrometheusRegistry; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -31,6 +34,8 @@ public final class PrometheusHttpServerBuilder { @Nullable private ExecutorService executor; private MemoryMode memoryMode = DEFAULT_MEMORY_MODE; @Nullable private HttpHandler defaultHandler; + private DefaultAggregationSelector defaultAggregationSelector = + DefaultAggregationSelector.getDefault(); PrometheusHttpServerBuilder() {} @@ -126,6 +131,19 @@ public PrometheusHttpServerBuilder setDefaultHandler(HttpHandler defaultHandler) return this; } + /** + * Set the {@link DefaultAggregationSelector} used for {@link + * MetricExporter#getDefaultAggregation(InstrumentType)}. + * + *

If unset, defaults to {@link DefaultAggregationSelector#getDefault()}. + */ + public PrometheusHttpServerBuilder setDefaultAggregationSelector( + DefaultAggregationSelector defaultAggregationSelector) { + requireNonNull(defaultAggregationSelector, "defaultAggregationSelector"); + this.defaultAggregationSelector = defaultAggregationSelector; + return this; + } + /** * Returns a new {@link PrometheusHttpServer} with the configuration of this builder which can be * registered with a {@link io.opentelemetry.sdk.metrics.SdkMeterProvider}. @@ -140,6 +158,7 @@ public PrometheusHttpServer build() { otelScopeEnabled, allowedResourceAttributesFilter, memoryMode, - defaultHandler); + defaultHandler, + defaultAggregationSelector); } } diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusMetricReaderProvider.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusMetricReaderProvider.java index 39d6c755e17..c06dc204212 100644 --- a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusMetricReaderProvider.java +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusMetricReaderProvider.java @@ -5,12 +5,19 @@ package io.opentelemetry.exporter.prometheus.internal; +import static io.opentelemetry.sdk.metrics.Aggregation.explicitBucketHistogram; + import io.opentelemetry.exporter.internal.ExporterBuilderUtil; import io.opentelemetry.exporter.prometheus.PrometheusHttpServer; import io.opentelemetry.exporter.prometheus.PrometheusHttpServerBuilder; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider; +import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector; import io.opentelemetry.sdk.metrics.export.MetricReader; +import io.opentelemetry.sdk.metrics.internal.aggregator.AggregationUtil; /** * SPI implementation for {@link PrometheusHttpServer}. @@ -32,6 +39,20 @@ public MetricReader createMetricReader(ConfigProperties config) { if (host != null) { prometheusBuilder.setHost(host); } + String defaultHistogramAggregation = + config.getString("otel.exporter.prometheus.metrics.default.histogram.aggregation"); + if (defaultHistogramAggregation != null) { + if (AggregationUtil.aggregationName(Aggregation.base2ExponentialBucketHistogram()) + .equalsIgnoreCase(defaultHistogramAggregation)) { + prometheusBuilder.setDefaultAggregationSelector( + DefaultAggregationSelector.getDefault() + .with(InstrumentType.HISTOGRAM, Aggregation.base2ExponentialBucketHistogram())); + } else if (!AggregationUtil.aggregationName(explicitBucketHistogram()) + .equalsIgnoreCase(defaultHistogramAggregation)) { + throw new ConfigurationException( + "Unrecognized default histogram aggregation: " + defaultHistogramAggregation); + } + } ExporterBuilderUtil.configureExporterMemoryMode(config, prometheusBuilder::setMemoryMode); diff --git a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java index 9b328493c1f..61a48ab2632 100644 --- a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java +++ b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java @@ -113,6 +113,9 @@ void invalidConfig() { assertThatThrownBy(() -> PrometheusHttpServer.builder().setHost("")) .isInstanceOf(IllegalArgumentException.class) .hasMessage("host must not be empty"); + assertThatThrownBy(() -> PrometheusHttpServer.builder().setDefaultAggregationSelector(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("defaultAggregationSelector"); } @Test diff --git a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/internal/PrometheusMetricReaderProviderTest.java b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/internal/PrometheusMetricReaderProviderTest.java index 5f450ab751f..0502bcf6540 100644 --- a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/internal/PrometheusMetricReaderProviderTest.java +++ b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/internal/PrometheusMetricReaderProviderTest.java @@ -7,14 +7,18 @@ import static org.assertj.core.api.Assertions.as; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import com.sun.net.httpserver.HttpServer; import io.opentelemetry.exporter.prometheus.PrometheusHttpServer; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import io.opentelemetry.sdk.common.export.MemoryMode; +import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.InstrumentType; import io.opentelemetry.sdk.metrics.export.MetricReader; import io.prometheus.metrics.exporter.httpserver.HTTPServer; import java.io.IOException; @@ -59,6 +63,8 @@ void createMetricReader_Default() throws IOException { assertThat(server.getAddress().getPort()).isEqualTo(9464); }); assertThat(metricReader.getMemoryMode()).isEqualTo(MemoryMode.IMMUTABLE_DATA); + assertThat(metricReader.getDefaultAggregation(InstrumentType.HISTOGRAM)) + .isEqualTo(Aggregation.defaultAggregation()); } } @@ -76,6 +82,9 @@ void createMetricReader_WithConfiguration() throws IOException { config.put("otel.exporter.prometheus.host", "localhost"); config.put("otel.exporter.prometheus.port", String.valueOf(port)); config.put("otel.java.experimental.exporter.memory_mode", "reusable_data"); + config.put( + "otel.exporter.prometheus.metrics.default.histogram.aggregation", + "BASE2_EXPONENTIAL_BUCKET_HISTOGRAM"); when(configProperties.getInt(any())).thenReturn(null); when(configProperties.getString(any())).thenReturn(null); @@ -91,6 +100,22 @@ void createMetricReader_WithConfiguration() throws IOException { assertThat(server.getAddress().getPort()).isEqualTo(port); }); assertThat(metricReader.getMemoryMode()).isEqualTo(MemoryMode.REUSABLE_DATA); + assertThat(metricReader.getDefaultAggregation(InstrumentType.HISTOGRAM)) + .isEqualTo(Aggregation.base2ExponentialBucketHistogram()); } } + + @Test + void createMetricReader_WithWrongConfiguration() throws IOException { + Map config = new HashMap<>(); + config.put("otel.exporter.prometheus.metrics.default.histogram.aggregation", "foo"); + + when(configProperties.getInt(any())).thenReturn(null); + when(configProperties.getString(any())).thenReturn(null); + + assertThatThrownBy( + () -> provider.createMetricReader(DefaultConfigProperties.createFromMap(config))) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining("Unrecognized default histogram aggregation:"); + } }