diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterProvider.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterProvider.java
index ebfaaacf296..eb286b09b94 100644
--- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterProvider.java
+++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterProvider.java
@@ -9,15 +9,19 @@
import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.PROTOCOL_GRPC;
import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.PROTOCOL_HTTP_PROTOBUF;
+import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.exporter.internal.ExporterBuilderUtil;
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder;
+import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
+import io.opentelemetry.sdk.autoconfigure.spi.internal.AutoConfigureListener;
import io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
+import java.util.concurrent.atomic.AtomicReference;
/**
* {@link MetricExporter} SPI implementation for {@link OtlpGrpcMetricExporter} and {@link
@@ -26,7 +30,11 @@
*
This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
-public class OtlpMetricExporterProvider implements ConfigurableMetricExporterProvider {
+public class OtlpMetricExporterProvider
+ implements ConfigurableMetricExporterProvider, AutoConfigureListener {
+
+ private final AtomicReference meterProviderRef =
+ new AtomicReference<>(MeterProvider.noop());
@Override
public MetricExporter createExporter(ConfigProperties config) {
@@ -51,6 +59,7 @@ public MetricExporter createExporter(ConfigProperties config) {
config, builder::setAggregationTemporalitySelector);
ExporterBuilderUtil.configureOtlpHistogramDefaultAggregation(
config, builder::setDefaultAggregationSelector);
+ builder.setMeterProvider(meterProviderRef::get);
return builder.build();
} else if (protocol.equals(PROTOCOL_GRPC)) {
@@ -72,6 +81,7 @@ public MetricExporter createExporter(ConfigProperties config) {
config, builder::setAggregationTemporalitySelector);
ExporterBuilderUtil.configureOtlpHistogramDefaultAggregation(
config, builder::setDefaultAggregationSelector);
+ builder.setMeterProvider(meterProviderRef::get);
return builder.build();
}
@@ -92,4 +102,9 @@ OtlpHttpMetricExporterBuilder httpBuilder() {
OtlpGrpcMetricExporterBuilder grpcBuilder() {
return OtlpGrpcMetricExporter.builder();
}
+
+ @Override
+ public void afterAutoConfigure(OpenTelemetrySdk sdk) {
+ meterProviderRef.set(sdk.getMeterProvider());
+ }
}
diff --git a/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterProviderTest.java b/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterProviderTest.java
index e028c6a9571..0bcd064c97d 100644
--- a/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterProviderTest.java
+++ b/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterProviderTest.java
@@ -5,31 +5,38 @@
package io.opentelemetry.exporter.otlp.internal;
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.singletonMap;
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.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import com.linecorp.armeria.testing.junit5.server.SelfSignedCertificateExtension;
+import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder;
+import io.opentelemetry.sdk.OpenTelemetrySdk;
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.export.MetricExporter;
import java.io.IOException;
+import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.cert.CertificateEncodingException;
import java.time.Duration;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -94,7 +101,7 @@ void createExporter_UnsupportedProtocol() {
() ->
provider.createExporter(
DefaultConfigProperties.createFromMap(
- Collections.singletonMap("otel.exporter.otlp.protocol", "foo"))))
+ singletonMap("otel.exporter.otlp.protocol", "foo"))))
.isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Unsupported OTLP metrics protocol: foo");
}
@@ -104,13 +111,13 @@ void createExporter_NoMocks() {
// Verifies createExporter after resetting the spy overrides
Mockito.reset(provider);
try (MetricExporter exporter =
- provider.createExporter(DefaultConfigProperties.createFromMap(Collections.emptyMap()))) {
+ provider.createExporter(DefaultConfigProperties.createFromMap(emptyMap()))) {
assertThat(exporter).isInstanceOf(OtlpGrpcMetricExporter.class);
}
try (MetricExporter exporter =
provider.createExporter(
DefaultConfigProperties.createFromMap(
- Collections.singletonMap("otel.exporter.otlp.protocol", "http/protobuf")))) {
+ singletonMap("otel.exporter.otlp.protocol", "http/protobuf")))) {
assertThat(exporter).isInstanceOf(OtlpHttpMetricExporter.class);
}
}
@@ -118,7 +125,7 @@ void createExporter_NoMocks() {
@Test
void createExporter_GrpcDefaults() {
try (MetricExporter exporter =
- provider.createExporter(DefaultConfigProperties.createFromMap(Collections.emptyMap()))) {
+ provider.createExporter(DefaultConfigProperties.createFromMap(emptyMap()))) {
assertThat(exporter).isInstanceOf(OtlpGrpcMetricExporter.class);
verify(grpcBuilder, times(1)).build();
verify(grpcBuilder).setComponentLoader(any());
@@ -204,8 +211,7 @@ void createExporter_HttpDefaults() {
try (MetricExporter exporter =
provider.createExporter(
DefaultConfigProperties.createFromMap(
- Collections.singletonMap(
- "otel.exporter.otlp.metrics.protocol", "http/protobuf")))) {
+ singletonMap("otel.exporter.otlp.metrics.protocol", "http/protobuf")))) {
assertThat(exporter).isInstanceOf(OtlpHttpMetricExporter.class);
verify(httpBuilder, times(1)).build();
verify(httpBuilder).setComponentLoader(any());
@@ -288,4 +294,107 @@ void createExporter_HttpWithSignalConfiguration() throws CertificateEncodingExce
}
Mockito.verifyNoInteractions(grpcBuilder);
}
+
+ @Test
+ void meterProviderRef_InitializedWithNoop() throws Exception {
+ AtomicReference meterProviderRef = getMeterProviderRef(provider);
+
+ assertThat(meterProviderRef.get()).isSameAs(MeterProvider.noop());
+ }
+
+ @Test
+ void afterAutoConfigure_UpdatesMeterProviderRef() throws Exception {
+ OpenTelemetrySdk sdk = OpenTelemetrySdk.builder().build();
+
+ AtomicReference meterProviderRef = getMeterProviderRef(provider);
+ assertThat(meterProviderRef.get()).isSameAs(MeterProvider.noop());
+
+ provider.afterAutoConfigure(sdk);
+
+ assertThat(meterProviderRef.get()).isSameAs(sdk.getMeterProvider());
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void createExporter_GrpcSetsMeterProviderSupplier() {
+ AtomicReference> capturedSupplier = new AtomicReference<>();
+
+ OpenTelemetrySdk sdk = OpenTelemetrySdk.builder().build();
+ provider.afterAutoConfigure(sdk);
+
+ doAnswer(
+ invocation -> {
+ capturedSupplier.set(invocation.getArgument(0));
+ return grpcBuilder;
+ })
+ .when(grpcBuilder)
+ .setMeterProvider(any(Supplier.class));
+
+ try (MetricExporter exporter =
+ provider.createExporter(DefaultConfigProperties.createFromMap(emptyMap()))) {
+
+ assertThat(exporter).isInstanceOf(OtlpGrpcMetricExporter.class);
+ assertThat(capturedSupplier.get()).isNotNull();
+ assertThat(capturedSupplier.get().get()).isSameAs(sdk.getMeterProvider());
+ }
+ Mockito.verifyNoInteractions(httpBuilder);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void createExporter_HttpSetsMeterProviderSupplier() {
+ AtomicReference> capturedSupplier = new AtomicReference<>();
+
+ OpenTelemetrySdk sdk = OpenTelemetrySdk.builder().build();
+ provider.afterAutoConfigure(sdk);
+
+ doAnswer(
+ invocation -> {
+ capturedSupplier.set(invocation.getArgument(0));
+ return httpBuilder;
+ })
+ .when(httpBuilder)
+ .setMeterProvider(any(Supplier.class));
+
+ try (MetricExporter exporter =
+ provider.createExporter(
+ DefaultConfigProperties.createFromMap(
+ singletonMap("otel.exporter.otlp.metrics.protocol", "http/protobuf")))) {
+
+ assertThat(exporter).isInstanceOf(OtlpHttpMetricExporter.class);
+ assertThat(capturedSupplier.get()).isNotNull();
+ assertThat(capturedSupplier.get().get()).isSameAs(sdk.getMeterProvider());
+ }
+ Mockito.verifyNoInteractions(grpcBuilder);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void meterProviderSupplier_ReturnsNoopBeforeAutoConfigure() {
+ AtomicReference> capturedSupplier = new AtomicReference<>();
+
+ doAnswer(
+ invocation -> {
+ capturedSupplier.set(invocation.getArgument(0));
+ return grpcBuilder;
+ })
+ .when(grpcBuilder)
+ .setMeterProvider(any(Supplier.class));
+
+ try (MetricExporter exporter =
+ provider.createExporter(DefaultConfigProperties.createFromMap(emptyMap()))) {
+
+ assertThat(exporter).isInstanceOf(OtlpGrpcMetricExporter.class);
+ assertThat(capturedSupplier.get()).isNotNull();
+ assertThat(capturedSupplier.get().get()).isSameAs(MeterProvider.noop());
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static AtomicReference getMeterProviderRef(
+ OtlpMetricExporterProvider provider) throws Exception {
+ Field field = OtlpMetricExporterProvider.class.getDeclaredField("meterProviderRef");
+ field.setAccessible(true);
+ return (AtomicReference) field.get(provider);
+ }
}
diff --git a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/ConfigurableMetricExporterTest.java b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/ConfigurableMetricExporterTest.java
index bc32ebc84a4..e46d0bd7718 100644
--- a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/ConfigurableMetricExporterTest.java
+++ b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/ConfigurableMetricExporterTest.java
@@ -10,6 +10,7 @@
import com.google.common.collect.ImmutableMap;
import io.opentelemetry.exporter.logging.LoggingMetricExporter;
+import io.opentelemetry.exporter.otlp.internal.OtlpMetricExporterProvider;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
import io.opentelemetry.internal.testing.CleanupExtension;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
@@ -52,7 +53,8 @@ void configureExporter_spiExporter() {
assertThat(spiHelper.getListeners())
.satisfiesExactlyInAnyOrder(
listener ->
- assertThat(listener).isInstanceOf(TestConfigurableMetricExporterProvider.class));
+ assertThat(listener).isInstanceOf(TestConfigurableMetricExporterProvider.class),
+ listener -> assertThat(listener).isInstanceOf(OtlpMetricExporterProvider.class));
}
}