Skip to content

Commit b6200c1

Browse files
authored
Update OtlpMetricExporterProvider to implement AutoConfigureListener (#7541)
1 parent d34fc34 commit b6200c1

File tree

3 files changed

+135
-9
lines changed

3 files changed

+135
-9
lines changed

exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterProvider.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,19 @@
99
import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.PROTOCOL_GRPC;
1010
import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.PROTOCOL_HTTP_PROTOBUF;
1111

12+
import io.opentelemetry.api.metrics.MeterProvider;
1213
import io.opentelemetry.exporter.internal.ExporterBuilderUtil;
1314
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
1415
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder;
1516
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
1617
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder;
18+
import io.opentelemetry.sdk.OpenTelemetrySdk;
1719
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
1820
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
21+
import io.opentelemetry.sdk.autoconfigure.spi.internal.AutoConfigureListener;
1922
import io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider;
2023
import io.opentelemetry.sdk.metrics.export.MetricExporter;
24+
import java.util.concurrent.atomic.AtomicReference;
2125

2226
/**
2327
* {@link MetricExporter} SPI implementation for {@link OtlpGrpcMetricExporter} and {@link
@@ -26,7 +30,11 @@
2630
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
2731
* at any time.
2832
*/
29-
public class OtlpMetricExporterProvider implements ConfigurableMetricExporterProvider {
33+
public class OtlpMetricExporterProvider
34+
implements ConfigurableMetricExporterProvider, AutoConfigureListener {
35+
36+
private final AtomicReference<MeterProvider> meterProviderRef =
37+
new AtomicReference<>(MeterProvider.noop());
3038

3139
@Override
3240
public MetricExporter createExporter(ConfigProperties config) {
@@ -51,6 +59,7 @@ public MetricExporter createExporter(ConfigProperties config) {
5159
config, builder::setAggregationTemporalitySelector);
5260
ExporterBuilderUtil.configureOtlpHistogramDefaultAggregation(
5361
config, builder::setDefaultAggregationSelector);
62+
builder.setMeterProvider(meterProviderRef::get);
5463

5564
return builder.build();
5665
} else if (protocol.equals(PROTOCOL_GRPC)) {
@@ -72,6 +81,7 @@ public MetricExporter createExporter(ConfigProperties config) {
7281
config, builder::setAggregationTemporalitySelector);
7382
ExporterBuilderUtil.configureOtlpHistogramDefaultAggregation(
7483
config, builder::setDefaultAggregationSelector);
84+
builder.setMeterProvider(meterProviderRef::get);
7585

7686
return builder.build();
7787
}
@@ -92,4 +102,9 @@ OtlpHttpMetricExporterBuilder httpBuilder() {
92102
OtlpGrpcMetricExporterBuilder grpcBuilder() {
93103
return OtlpGrpcMetricExporter.builder();
94104
}
105+
106+
@Override
107+
public void afterAutoConfigure(OpenTelemetrySdk sdk) {
108+
meterProviderRef.set(sdk.getMeterProvider());
109+
}
95110
}

exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterProviderTest.java

Lines changed: 116 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,38 @@
55

66
package io.opentelemetry.exporter.otlp.internal;
77

8+
import static java.util.Collections.emptyMap;
9+
import static java.util.Collections.singletonMap;
810
import static org.assertj.core.api.Assertions.assertThat;
911
import static org.assertj.core.api.Assertions.assertThatThrownBy;
1012
import static org.mockito.ArgumentMatchers.any;
13+
import static org.mockito.Mockito.doAnswer;
1114
import static org.mockito.Mockito.doReturn;
1215
import static org.mockito.Mockito.never;
1316
import static org.mockito.Mockito.times;
1417
import static org.mockito.Mockito.verify;
1518

1619
import com.linecorp.armeria.testing.junit5.server.SelfSignedCertificateExtension;
20+
import io.opentelemetry.api.metrics.MeterProvider;
1721
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
1822
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder;
1923
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
2024
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder;
25+
import io.opentelemetry.sdk.OpenTelemetrySdk;
2126
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
2227
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
2328
import io.opentelemetry.sdk.common.export.MemoryMode;
2429
import io.opentelemetry.sdk.metrics.export.MetricExporter;
2530
import java.io.IOException;
31+
import java.lang.reflect.Field;
2632
import java.nio.file.Files;
2733
import java.nio.file.Path;
2834
import java.security.cert.CertificateEncodingException;
2935
import java.time.Duration;
30-
import java.util.Collections;
3136
import java.util.HashMap;
3237
import java.util.Map;
38+
import java.util.concurrent.atomic.AtomicReference;
39+
import java.util.function.Supplier;
3340
import org.junit.jupiter.api.BeforeEach;
3441
import org.junit.jupiter.api.Test;
3542
import org.junit.jupiter.api.extension.ExtendWith;
@@ -94,7 +101,7 @@ void createExporter_UnsupportedProtocol() {
94101
() ->
95102
provider.createExporter(
96103
DefaultConfigProperties.createFromMap(
97-
Collections.singletonMap("otel.exporter.otlp.protocol", "foo"))))
104+
singletonMap("otel.exporter.otlp.protocol", "foo"))))
98105
.isInstanceOf(ConfigurationException.class)
99106
.hasMessageContaining("Unsupported OTLP metrics protocol: foo");
100107
}
@@ -104,21 +111,21 @@ void createExporter_NoMocks() {
104111
// Verifies createExporter after resetting the spy overrides
105112
Mockito.reset(provider);
106113
try (MetricExporter exporter =
107-
provider.createExporter(DefaultConfigProperties.createFromMap(Collections.emptyMap()))) {
114+
provider.createExporter(DefaultConfigProperties.createFromMap(emptyMap()))) {
108115
assertThat(exporter).isInstanceOf(OtlpGrpcMetricExporter.class);
109116
}
110117
try (MetricExporter exporter =
111118
provider.createExporter(
112119
DefaultConfigProperties.createFromMap(
113-
Collections.singletonMap("otel.exporter.otlp.protocol", "http/protobuf")))) {
120+
singletonMap("otel.exporter.otlp.protocol", "http/protobuf")))) {
114121
assertThat(exporter).isInstanceOf(OtlpHttpMetricExporter.class);
115122
}
116123
}
117124

118125
@Test
119126
void createExporter_GrpcDefaults() {
120127
try (MetricExporter exporter =
121-
provider.createExporter(DefaultConfigProperties.createFromMap(Collections.emptyMap()))) {
128+
provider.createExporter(DefaultConfigProperties.createFromMap(emptyMap()))) {
122129
assertThat(exporter).isInstanceOf(OtlpGrpcMetricExporter.class);
123130
verify(grpcBuilder, times(1)).build();
124131
verify(grpcBuilder).setComponentLoader(any());
@@ -204,8 +211,7 @@ void createExporter_HttpDefaults() {
204211
try (MetricExporter exporter =
205212
provider.createExporter(
206213
DefaultConfigProperties.createFromMap(
207-
Collections.singletonMap(
208-
"otel.exporter.otlp.metrics.protocol", "http/protobuf")))) {
214+
singletonMap("otel.exporter.otlp.metrics.protocol", "http/protobuf")))) {
209215
assertThat(exporter).isInstanceOf(OtlpHttpMetricExporter.class);
210216
verify(httpBuilder, times(1)).build();
211217
verify(httpBuilder).setComponentLoader(any());
@@ -288,4 +294,107 @@ void createExporter_HttpWithSignalConfiguration() throws CertificateEncodingExce
288294
}
289295
Mockito.verifyNoInteractions(grpcBuilder);
290296
}
297+
298+
@Test
299+
void meterProviderRef_InitializedWithNoop() throws Exception {
300+
AtomicReference<MeterProvider> meterProviderRef = getMeterProviderRef(provider);
301+
302+
assertThat(meterProviderRef.get()).isSameAs(MeterProvider.noop());
303+
}
304+
305+
@Test
306+
void afterAutoConfigure_UpdatesMeterProviderRef() throws Exception {
307+
OpenTelemetrySdk sdk = OpenTelemetrySdk.builder().build();
308+
309+
AtomicReference<MeterProvider> meterProviderRef = getMeterProviderRef(provider);
310+
assertThat(meterProviderRef.get()).isSameAs(MeterProvider.noop());
311+
312+
provider.afterAutoConfigure(sdk);
313+
314+
assertThat(meterProviderRef.get()).isSameAs(sdk.getMeterProvider());
315+
}
316+
317+
@Test
318+
@SuppressWarnings("unchecked")
319+
void createExporter_GrpcSetsMeterProviderSupplier() {
320+
AtomicReference<Supplier<MeterProvider>> capturedSupplier = new AtomicReference<>();
321+
322+
OpenTelemetrySdk sdk = OpenTelemetrySdk.builder().build();
323+
provider.afterAutoConfigure(sdk);
324+
325+
doAnswer(
326+
invocation -> {
327+
capturedSupplier.set(invocation.getArgument(0));
328+
return grpcBuilder;
329+
})
330+
.when(grpcBuilder)
331+
.setMeterProvider(any(Supplier.class));
332+
333+
try (MetricExporter exporter =
334+
provider.createExporter(DefaultConfigProperties.createFromMap(emptyMap()))) {
335+
336+
assertThat(exporter).isInstanceOf(OtlpGrpcMetricExporter.class);
337+
assertThat(capturedSupplier.get()).isNotNull();
338+
assertThat(capturedSupplier.get().get()).isSameAs(sdk.getMeterProvider());
339+
}
340+
Mockito.verifyNoInteractions(httpBuilder);
341+
}
342+
343+
@Test
344+
@SuppressWarnings("unchecked")
345+
void createExporter_HttpSetsMeterProviderSupplier() {
346+
AtomicReference<Supplier<MeterProvider>> capturedSupplier = new AtomicReference<>();
347+
348+
OpenTelemetrySdk sdk = OpenTelemetrySdk.builder().build();
349+
provider.afterAutoConfigure(sdk);
350+
351+
doAnswer(
352+
invocation -> {
353+
capturedSupplier.set(invocation.getArgument(0));
354+
return httpBuilder;
355+
})
356+
.when(httpBuilder)
357+
.setMeterProvider(any(Supplier.class));
358+
359+
try (MetricExporter exporter =
360+
provider.createExporter(
361+
DefaultConfigProperties.createFromMap(
362+
singletonMap("otel.exporter.otlp.metrics.protocol", "http/protobuf")))) {
363+
364+
assertThat(exporter).isInstanceOf(OtlpHttpMetricExporter.class);
365+
assertThat(capturedSupplier.get()).isNotNull();
366+
assertThat(capturedSupplier.get().get()).isSameAs(sdk.getMeterProvider());
367+
}
368+
Mockito.verifyNoInteractions(grpcBuilder);
369+
}
370+
371+
@Test
372+
@SuppressWarnings("unchecked")
373+
void meterProviderSupplier_ReturnsNoopBeforeAutoConfigure() {
374+
AtomicReference<Supplier<MeterProvider>> capturedSupplier = new AtomicReference<>();
375+
376+
doAnswer(
377+
invocation -> {
378+
capturedSupplier.set(invocation.getArgument(0));
379+
return grpcBuilder;
380+
})
381+
.when(grpcBuilder)
382+
.setMeterProvider(any(Supplier.class));
383+
384+
try (MetricExporter exporter =
385+
provider.createExporter(DefaultConfigProperties.createFromMap(emptyMap()))) {
386+
387+
assertThat(exporter).isInstanceOf(OtlpGrpcMetricExporter.class);
388+
assertThat(capturedSupplier.get()).isNotNull();
389+
assertThat(capturedSupplier.get().get()).isSameAs(MeterProvider.noop());
390+
}
391+
}
392+
393+
@SuppressWarnings("unchecked")
394+
private static AtomicReference<MeterProvider> getMeterProviderRef(
395+
OtlpMetricExporterProvider provider) throws Exception {
396+
Field field = OtlpMetricExporterProvider.class.getDeclaredField("meterProviderRef");
397+
field.setAccessible(true);
398+
return (AtomicReference<MeterProvider>) field.get(provider);
399+
}
291400
}

sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/ConfigurableMetricExporterTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import com.google.common.collect.ImmutableMap;
1212
import io.opentelemetry.exporter.logging.LoggingMetricExporter;
13+
import io.opentelemetry.exporter.otlp.internal.OtlpMetricExporterProvider;
1314
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
1415
import io.opentelemetry.internal.testing.CleanupExtension;
1516
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
@@ -52,7 +53,8 @@ void configureExporter_spiExporter() {
5253
assertThat(spiHelper.getListeners())
5354
.satisfiesExactlyInAnyOrder(
5455
listener ->
55-
assertThat(listener).isInstanceOf(TestConfigurableMetricExporterProvider.class));
56+
assertThat(listener).isInstanceOf(TestConfigurableMetricExporterProvider.class),
57+
listener -> assertThat(listener).isInstanceOf(OtlpMetricExporterProvider.class));
5658
}
5759
}
5860

0 commit comments

Comments
 (0)