From d70fe5b029ad4e4be81bdb76b27e585d8714ba16 Mon Sep 17 00:00:00 2001 From: jack-berg <34418638+jack-berg@users.noreply.github.com> Date: Tue, 6 May 2025 14:19:01 -0500 Subject: [PATCH] Fix Otlp*MetricExporterBuilderTests (#7313) --- .../exporter/internal/ExporterMetrics.java | 8 +- exporters/otlp/all/build.gradle.kts | 1 + .../OtlpHttpMetricExporterBuilderTest.java | 77 ++++++++++++---- .../OtlpGrpcMetricExporterBuilderTest.java | 91 +++++++++++++++---- 4 files changed, 136 insertions(+), 41 deletions(-) diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/ExporterMetrics.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/ExporterMetrics.java index f2cee48131..1075ac39e3 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/ExporterMetrics.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/ExporterMetrics.java @@ -87,9 +87,11 @@ public class ExporterMetrics { } private Meter meter() { - return meterProviderSupplier - .get() - .get("io.opentelemetry.exporters." + exporterName + "-" + transportName); + MeterProvider meterProvider = meterProviderSupplier.get(); + if (meterProvider == null) { + meterProvider = MeterProvider.noop(); + } + return meterProvider.get("io.opentelemetry.exporters." + exporterName + "-" + transportName); } private static boolean isNoop(LongCounter counter) { diff --git a/exporters/otlp/all/build.gradle.kts b/exporters/otlp/all/build.gradle.kts index eb9ad982c6..d6a51932a0 100644 --- a/exporters/otlp/all/build.gradle.kts +++ b/exporters/otlp/all/build.gradle.kts @@ -26,6 +26,7 @@ dependencies { testImplementation(project(":exporters:otlp:testing-internal")) testImplementation("com.linecorp.armeria:armeria-junit5") + testImplementation("com.linecorp.armeria:armeria-grpc-protocol") testImplementation("io.grpc:grpc-stub") jmhImplementation(project(":sdk:testing")) diff --git a/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterBuilderTest.java b/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterBuilderTest.java index fefd30d44d..4e9e461589 100644 --- a/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterBuilderTest.java +++ b/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterBuilderTest.java @@ -12,11 +12,16 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import com.linecorp.armeria.common.HttpResponse; +import com.linecorp.armeria.common.HttpStatus; +import com.linecorp.armeria.server.ServerBuilder; +import com.linecorp.armeria.testing.junit5.server.ServerExtension; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.metrics.LongCounter; @@ -36,9 +41,13 @@ import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; import io.opentelemetry.sdk.metrics.internal.data.ImmutableGaugeData; import io.opentelemetry.sdk.resources.Resource; import java.util.Collection; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.Mockito; class OtlpHttpMetricExporterBuilderTest { @@ -53,11 +62,31 @@ class OtlpHttpMetricExporterBuilderTest { "test", ImmutableGaugeData.empty())); + @RegisterExtension + private static final ServerExtension server = + new ServerExtension() { + @Override + protected void configure(ServerBuilder sb) { + sb.service("/v1/metrics", (ctx, req) -> HttpResponse.of(HttpStatus.OK)); + sb.http(0); + } + }; + private final SdkMeterProvider meterProvider = mock(SdkMeterProvider.class); private final Meter meter = mock(Meter.class); private final LongCounterBuilder counterBuilder = mock(LongCounterBuilder.class); private final LongCounter counter = mock(LongCounter.class); + @BeforeEach + void setup() { + GlobalOpenTelemetry.resetForTest(); + } + + @AfterEach + void cleanup() { + GlobalOpenTelemetry.resetForTest(); + } + @Test void setMeterProvider_null() { OtlpHttpMetricExporterBuilder builder = OtlpHttpMetricExporter.builder(); @@ -74,25 +103,29 @@ class OtlpHttpMetricExporterBuilderTest { @Test void setMeterProvider() { when(meterProvider.get(any())).thenReturn(meter); - when(meter.counterBuilder(eq("otlp.exporter.seen"))).thenReturn(counterBuilder); + when(meter.counterBuilder(any())).thenReturn(counterBuilder); when(counterBuilder.build()).thenReturn(counter); try (OtlpHttpMetricExporter exporter = - OtlpHttpMetricExporter.builder().setMeterProvider(meterProvider).build()) { + OtlpHttpMetricExporter.builder() + .setMeterProvider(meterProvider) + .setEndpoint("http://localhost:" + server.httpPort() + "/v1/metrics") + .build()) { verifyNoInteractions(meterProvider, meter, counterBuilder, counter); // Collection before MeterProvider is initialized. when(meterProvider.get(any())).thenReturn(MeterProvider.noop().get("test")); - exporter.export(DATA_SET); + exporter.export(DATA_SET).join(10, TimeUnit.SECONDS); verifyNoInteractions(meter, counterBuilder, counter); // Collection after MeterProvider is initialized. when(meterProvider.get(any())).thenReturn(meter); - exporter.export(DATA_SET); + exporter.export(DATA_SET).join(10, TimeUnit.SECONDS); - verify(meter).counterBuilder(eq("otlp.exporter.seen")); - verify(counter).add(eq(1L), any()); + verify(meter).counterBuilder("otlp.exporter.seen"); + verify(meter).counterBuilder("otlp.exporter.exported"); + verify(counter, times(2)).add(eq(1L), any()); verifyNoMoreInteractions(meter, counter); } } @@ -100,27 +133,31 @@ class OtlpHttpMetricExporterBuilderTest { @Test void setMeterProvider_supplier() { when(meterProvider.get(any())).thenReturn(meter); - when(meter.counterBuilder(eq("otlp.exporter.seen"))).thenReturn(counterBuilder); + when(meter.counterBuilder(any())).thenReturn(counterBuilder); when(counterBuilder.build()).thenReturn(counter); @SuppressWarnings("unchecked") Supplier provider = mock(Supplier.class); try (OtlpHttpMetricExporter exporter = - OtlpHttpMetricExporter.builder().setMeterProvider(provider).build()) { + OtlpHttpMetricExporter.builder() + .setMeterProvider(provider) + .setEndpoint("http://localhost:" + server.httpPort() + "/v1/metrics") + .build()) { verifyNoInteractions(provider, meterProvider, meter, counterBuilder, counter); // Collection before MeterProvider is initialized. when(provider.get()).thenReturn(MeterProvider.noop()); - exporter.export(DATA_SET); + exporter.export(DATA_SET).join(10, TimeUnit.SECONDS); verifyNoInteractions(meterProvider, meter, counterBuilder, counter); // Collection after MeterProvider is initialized. when(provider.get()).thenReturn(meterProvider); - exporter.export(DATA_SET); + exporter.export(DATA_SET).join(10, TimeUnit.SECONDS); - verify(meter).counterBuilder(eq("otlp.exporter.seen")); - verify(counter).add(eq(1L), any()); + verify(meter).counterBuilder("otlp.exporter.seen"); + verify(meter).counterBuilder("otlp.exporter.exported"); + verify(counter, times(2)).add(eq(1L), any()); verifyNoMoreInteractions(meter, counter); } } @@ -145,19 +182,21 @@ class OtlpHttpMetricExporterBuilderTest { } }); when(meterProvider.get(any())).thenReturn(meter); - when(meter.counterBuilder(eq("otlp.exporter.seen"))).thenReturn(counterBuilder); + when(meter.counterBuilder(any())).thenReturn(counterBuilder); when(counterBuilder.build()).thenReturn(counter); - try (OtlpHttpMetricExporter exporter = OtlpHttpMetricExporter.builder().build()) { + try (OtlpHttpMetricExporter exporter = + OtlpHttpMetricExporter.builder() + .setEndpoint("http://localhost:" + server.httpPort() + "/v1/metrics") + .build()) { verifyNoInteractions(meterProvider, meter, counterBuilder, counter); - exporter.export(DATA_SET); + exporter.export(DATA_SET).join(10, TimeUnit.SECONDS); - verify(meter).counterBuilder(eq("otlp.exporter.seen")); - verify(counter).add(eq(1L), any()); + verify(meter).counterBuilder("otlp.exporter.seen"); + verify(meter).counterBuilder("otlp.exporter.exported"); + verify(counter, times(2)).add(eq(1L), any()); verifyNoMoreInteractions(meter, counter); - } finally { - GlobalOpenTelemetry.resetForTest(); } } diff --git a/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterBuilderTest.java b/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterBuilderTest.java index c12d8c4c6b..ea0d2dc4d1 100644 --- a/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterBuilderTest.java +++ b/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterBuilderTest.java @@ -12,11 +12,17 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import com.linecorp.armeria.server.ServerBuilder; +import com.linecorp.armeria.server.ServiceRequestContext; +import com.linecorp.armeria.server.grpc.protocol.AbstractUnaryGrpcService; +import com.linecorp.armeria.server.logging.LoggingService; +import com.linecorp.armeria.testing.junit5.server.ServerExtension; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.metrics.LongCounter; @@ -25,6 +31,7 @@ import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.api.trace.TracerProvider; import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceResponse; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.metrics.SdkMeterProvider; @@ -36,9 +43,15 @@ import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; import io.opentelemetry.sdk.metrics.internal.data.ImmutableGaugeData; import io.opentelemetry.sdk.resources.Resource; import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.Mockito; class OtlpGrpcMetricExporterBuilderTest { @@ -53,11 +66,41 @@ class OtlpGrpcMetricExporterBuilderTest { "test", ImmutableGaugeData.empty())); + @RegisterExtension + private static final ServerExtension server = + new ServerExtension() { + @Override + protected void configure(ServerBuilder sb) { + sb.service( + "/opentelemetry.proto.collector.metrics.v1.MetricsService/Export", + new AbstractUnaryGrpcService() { + @Override + protected CompletionStage handleMessage( + ServiceRequestContext ctx, byte[] message) { + return CompletableFuture.completedFuture( + ExportMetricsServiceResponse.getDefaultInstance().toByteArray()); + } + }); + sb.http(0); + sb.decorator(LoggingService.newDecorator()); + } + }; + private final MeterProvider meterProvider = mock(MeterProvider.class); private final Meter meter = mock(Meter.class); private final LongCounterBuilder counterBuilder = mock(LongCounterBuilder.class); private final LongCounter counter = mock(LongCounter.class); + @BeforeEach + void setup() { + GlobalOpenTelemetry.resetForTest(); + } + + @AfterEach + void cleanup() { + GlobalOpenTelemetry.resetForTest(); + } + @Test void setMeterProvider_null() { OtlpGrpcMetricExporterBuilder builder = OtlpGrpcMetricExporter.builder(); @@ -74,25 +117,29 @@ class OtlpGrpcMetricExporterBuilderTest { @Test void setMeterProvider() { when(meterProvider.get(any())).thenReturn(meter); - when(meter.counterBuilder(eq("otlp.exporter.seen"))).thenReturn(counterBuilder); + when(meter.counterBuilder(any())).thenReturn(counterBuilder); when(counterBuilder.build()).thenReturn(counter); try (OtlpGrpcMetricExporter exporter = - OtlpGrpcMetricExporter.builder().setMeterProvider(meterProvider).build()) { + OtlpGrpcMetricExporter.builder() + .setMeterProvider(meterProvider) + .setEndpoint("http://localhost:" + server.httpPort()) + .build()) { verifyNoInteractions(meterProvider, meter, counterBuilder, counter); // Collection before MeterProvider is initialized. when(meterProvider.get(any())).thenReturn(MeterProvider.noop().get("test")); - exporter.export(DATA_SET); + exporter.export(DATA_SET).join(10, TimeUnit.SECONDS); verifyNoInteractions(meter, counterBuilder, counter); // Collection after MeterProvider is initialized. when(meterProvider.get(any())).thenReturn(meter); - exporter.export(DATA_SET); + exporter.export(DATA_SET).join(10, TimeUnit.SECONDS); - verify(meter).counterBuilder(eq("otlp.exporter.seen")); - verify(counter).add(eq(1L), any()); + verify(meter).counterBuilder("otlp.exporter.seen"); + verify(meter).counterBuilder("otlp.exporter.exported"); + verify(counter, times(2)).add(eq(1L), any()); verifyNoMoreInteractions(meter, counter); } } @@ -100,27 +147,31 @@ class OtlpGrpcMetricExporterBuilderTest { @Test void setMeterProvider_supplier() { when(meterProvider.get(any())).thenReturn(meter); - when(meter.counterBuilder(eq("otlp.exporter.seen"))).thenReturn(counterBuilder); + when(meter.counterBuilder(any())).thenReturn(counterBuilder); when(counterBuilder.build()).thenReturn(counter); @SuppressWarnings("unchecked") Supplier provider = mock(Supplier.class); try (OtlpGrpcMetricExporter exporter = - OtlpGrpcMetricExporter.builder().setMeterProvider(provider).build()) { + OtlpGrpcMetricExporter.builder() + .setMeterProvider(provider) + .setEndpoint("http://localhost:" + server.httpPort()) + .build()) { verifyNoInteractions(provider, meterProvider, meter, counterBuilder, counter); // Collection before MeterProvider is initialized. when(provider.get()).thenReturn(MeterProvider.noop()); - exporter.export(DATA_SET); + exporter.export(DATA_SET).join(10, TimeUnit.SECONDS); verifyNoInteractions(meterProvider, meter, counterBuilder, counter); // Collection after MeterProvider is initialized. when(provider.get()).thenReturn(meterProvider); - exporter.export(DATA_SET); + exporter.export(DATA_SET).join(10, TimeUnit.SECONDS); - verify(meter).counterBuilder(eq("otlp.exporter.seen")); - verify(counter).add(eq(1L), any()); + verify(meter).counterBuilder("otlp.exporter.seen"); + verify(meter).counterBuilder("otlp.exporter.exported"); + verify(counter, times(2)).add(eq(1L), any()); verifyNoMoreInteractions(meter, counter); } } @@ -145,19 +196,21 @@ class OtlpGrpcMetricExporterBuilderTest { } }); when(meterProvider.get(any())).thenReturn(meter); - when(meter.counterBuilder(eq("otlp.exporter.seen"))).thenReturn(counterBuilder); + when(meter.counterBuilder(any())).thenReturn(counterBuilder); when(counterBuilder.build()).thenReturn(counter); - try (OtlpGrpcMetricExporter exporter = OtlpGrpcMetricExporter.builder().build()) { + try (OtlpGrpcMetricExporter exporter = + OtlpGrpcMetricExporter.builder() + .setEndpoint("http://localhost:" + server.httpPort()) + .build()) { verifyNoInteractions(meterProvider, meter, counterBuilder, counter); - exporter.export(DATA_SET); + exporter.export(DATA_SET).join(10, TimeUnit.SECONDS); - verify(meter).counterBuilder(eq("otlp.exporter.seen")); - verify(counter).add(eq(1L), any()); + verify(meter).counterBuilder("otlp.exporter.seen"); + verify(meter).counterBuilder("otlp.exporter.exported"); + verify(counter, times(2)).add(eq(1L), any()); verifyNoMoreInteractions(meter, counter); - } finally { - GlobalOpenTelemetry.resetForTest(); } }