Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -65,7 +68,8 @@ public static PrometheusHttpServerBuilder builder() {
boolean otelScopeEnabled,
@Nullable Predicate<String> allowedResourceAttributesFilter,
MemoryMode memoryMode,
@Nullable HttpHandler defaultHandler) {
@Nullable HttpHandler defaultHandler,
DefaultAggregationSelector defaultAggregationSelector) {
this.builder = builder;
this.prometheusMetricReader =
new PrometheusMetricReader(otelScopeEnabled, allowedResourceAttributesFilter);
Expand All @@ -92,13 +96,19 @@ public static PrometheusHttpServerBuilder builder() {
} catch (IOException e) {
throw new UncheckedIOException("Could not create Prometheus HTTP server", e);
}
this.defaultAggregationSelector = defaultAggregationSelector;
}

@Override
public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) {
return prometheusMetricReader.getAggregationTemporality(instrumentType);
}

@Override
public Aggregation getDefaultAggregation(InstrumentType instrumentType) {
return defaultAggregationSelector.getDefaultAggregation(instrumentType);
}

@Override
public MemoryMode getMemoryMode() {
return memoryMode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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() {}

Expand All @@ -41,6 +46,7 @@ public final class PrometheusHttpServerBuilder {
this.otelScopeEnabled = builder.otelScopeEnabled;
this.allowedResourceAttributesFilter = builder.allowedResourceAttributesFilter;
this.executor = builder.executor;
this.defaultAggregationSelector = builder.defaultAggregationSelector;
}

/** Sets the host to bind to. If unset, defaults to {@value #DEFAULT_HOST}. */
Expand Down Expand Up @@ -126,6 +132,19 @@ public PrometheusHttpServerBuilder setDefaultHandler(HttpHandler defaultHandler)
return this;
}

/**
* Set the {@link DefaultAggregationSelector} used for {@link
* MetricExporter#getDefaultAggregation(InstrumentType)}.
*
* <p>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}.
Expand All @@ -140,6 +159,7 @@ public PrometheusHttpServer build() {
otelScopeEnabled,
allowedResourceAttributesFilter,
memoryMode,
defaultHandler);
defaultHandler,
defaultAggregationSelector);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@

package io.opentelemetry.exporter.prometheus.internal;

import static io.opentelemetry.sdk.metrics.Aggregation.explicitBucketHistogram;

import io.opentelemetry.exporter.prometheus.PrometheusHttpServer;
import io.opentelemetry.exporter.prometheus.PrometheusHttpServerBuilder;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties;
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;

/**
* File configuration SPI implementation for {@link PrometheusHttpServer}.
Expand Down Expand Up @@ -41,6 +48,19 @@ public MetricReader create(StructuredConfigProperties config) {
if (host != null) {
prometheusBuilder.setHost(host);
}
String defaultHistogramAggregation = config.getString("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);
}
}

return prometheusBuilder.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}.
Expand All @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@
import io.opentelemetry.internal.testing.slf4j.SuppressLogger;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.common.export.MemoryMode;
import io.opentelemetry.sdk.metrics.Aggregation;
import io.opentelemetry.sdk.metrics.InstrumentType;
import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.metrics.export.CollectionRegistration;
import io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector;
import io.opentelemetry.sdk.metrics.internal.data.ImmutableDoublePointData;
import io.opentelemetry.sdk.metrics.internal.data.ImmutableGaugeData;
import io.opentelemetry.sdk.metrics.internal.data.ImmutableLongPointData;
Expand Down Expand Up @@ -113,6 +116,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
Expand Down Expand Up @@ -500,10 +506,14 @@ private static List<MetricData> generateTestData() {

@Test
void toBuilder() {
DefaultAggregationSelector defaultAggregationSelector =
DefaultAggregationSelector.getDefault()
.with(InstrumentType.HISTOGRAM, Aggregation.base2ExponentialBucketHistogram());
PrometheusHttpServerBuilder builder = PrometheusHttpServer.builder();
builder.setHost("localhost");
builder.setPort(1234);
builder.setOtelScopeEnabled(false);
builder.setDefaultAggregationSelector(defaultAggregationSelector);

Predicate<String> resourceAttributesFilter = s -> false;
builder.setAllowedResourceAttributesFilter(resourceAttributesFilter);
Expand All @@ -524,6 +534,7 @@ void toBuilder() {
.hasFieldOrPropertyWithValue("otelScopeEnabled", false)
.hasFieldOrPropertyWithValue("allowedResourceAttributesFilter", resourceAttributesFilter)
.hasFieldOrPropertyWithValue("executor", executor)
.hasFieldOrPropertyWithValue("prometheusRegistry", prometheusRegistry);
.hasFieldOrPropertyWithValue("prometheusRegistry", prometheusRegistry)
.hasFieldOrPropertyWithValue("defaultAggregationSelector", defaultAggregationSelector);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
}
}

Expand All @@ -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);
Expand All @@ -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<String, String> 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:");
}
}