diff --git a/buildSrc/src/main/kotlin/otel.android-app-conventions.gradle.kts b/buildSrc/src/main/kotlin/otel.android-app-conventions.gradle.kts index 9b38079c3..b84086070 100644 --- a/buildSrc/src/main/kotlin/otel.android-app-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/otel.android-app-conventions.gradle.kts @@ -32,6 +32,8 @@ android { val libs = extensions.getByType().named("libs") dependencies { + androidTestImplementation(libs.findLibrary("androidx-test-core").get()) + androidTestImplementation(libs.findLibrary("androidx-test-rules").get()) androidTestImplementation(libs.findLibrary("androidx-test-runner").get()) androidTestImplementation(libs.findLibrary("opentelemetry-sdk-testing").get()) coreLibraryDesugaring(libs.findLibrary("desugarJdkLibs").get()) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8dde25af6..82eddd260 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,16 +23,16 @@ androidx-lifecycle-process = "androidx.lifecycle:lifecycle-process:2.8.4" findbugs-jsr305 = "com.google.code.findbugs:jsr305:3.0.2" byteBuddy = { module = "net.bytebuddy:byte-buddy", version.ref = "byteBuddy" } okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } -opentelemetry-instrumentation-api = { module = "io.opentelemetry.instrumentation:opentelemetry-instrumentation-api"} -opentelemetry-instrumentation-apiSemconv = { module = "io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator", version.ref = "opentelemetry-instrumentation-alpha"} +opentelemetry-instrumentation-api = { module = "io.opentelemetry.instrumentation:opentelemetry-instrumentation-api" } +opentelemetry-instrumentation-apiSemconv = { module = "io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator", version.ref = "opentelemetry-instrumentation-alpha" } opentelemetry-instrumentation-okhttp = { module = "io.opentelemetry.instrumentation:opentelemetry-okhttp-3.0", version.ref = "opentelemetry-instrumentation-alpha" } opentelemetry-semconv = { module = "io.opentelemetry.semconv:opentelemetry-semconv", version.ref = "opentelemetry-semconv" } opentelemetry-semconv-incubating = { module = "io.opentelemetry.semconv:opentelemetry-semconv-incubating", version.ref = "opentelemetry-semconv" } -opentelemetry-api = { module = "io.opentelemetry:opentelemetry-api"} +opentelemetry-api = { module = "io.opentelemetry:opentelemetry-api" } opentelemetry-api-incubator = { module = "io.opentelemetry:opentelemetry-api-incubator" } opentelemetry-sdk-extension-incubator = { module = "io.opentelemetry:opentelemetry-sdk-extension-incubator", version.ref = "opentelemetry-alpha" } -opentelemetry-sdk = { module = "io.opentelemetry:opentelemetry-sdk"} -opentelemetry-context = { module = "io.opentelemetry:opentelemetry-context", version.ref = "opentelemetry"} +opentelemetry-sdk = { module = "io.opentelemetry:opentelemetry-sdk" } +opentelemetry-context = { module = "io.opentelemetry:opentelemetry-context", version.ref = "opentelemetry" } opentelemetry-exporter-logging = { module = "io.opentelemetry:opentelemetry-exporter-logging" } opentelemetry-diskBuffering = { module = "io.opentelemetry.contrib:opentelemetry-disk-buffering", version.ref = "opentelemetry-contrib" } opentelemetry-exporter-otlp = { module = "io.opentelemetry:opentelemetry-exporter-otlp", version.ref = "opentelemetry" } @@ -43,6 +43,7 @@ auto-service-processor = { module = "com.google.auto.service:auto-service", vers #Test tools opentelemetry-sdk-testing = { module = "io.opentelemetry:opentelemetry-sdk-testing", version.ref = "opentelemetry" } androidx-test-core = "androidx.test:core:1.6.1" +androidx-test-rules = "androidx.test:rules:1.6.1" androidx-test-runner = "androidx.test:runner:1.6.1" androidx-junit = "androidx.test.ext:junit:1.2.1" mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" } diff --git a/instrumentation/okhttp/okhttp-3.0/README.md b/instrumentation/okhttp/okhttp-3.0/README.md index fc74ed548..6b8456bfa 100644 --- a/instrumentation/okhttp/okhttp-3.0/README.md +++ b/instrumentation/okhttp/okhttp-3.0/README.md @@ -40,4 +40,17 @@ automatically. ### Configuration You can configure the automatic instrumentation by using the setters -in [OkHttpInstrumentationConfig](library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/OkHttpInstrumentationConfig.java)). +from +the [OkHttpInstrumentation](library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/OkHttpInstrumentation.java)) +instance provided via the AndroidInstrumentationLoader as shown below: + +```java +OkHttpInstrumentation instrumentation = AndroidInstrumentationLoader.getInstrumentation(OkHttpInstrumentation.class); + +// Call `instrumentation` setters. +``` + +> [!NOTE] +> You must make sure to apply any configurations **before** initializing your OpenTelemetryRum +> instance (i.e. calling OpenTelemetryRum.builder()...build()). Otherwise your configs won't be +> taken into account during the RUM initialization process. diff --git a/instrumentation/okhttp/okhttp-3.0/library/build.gradle.kts b/instrumentation/okhttp/okhttp-3.0/library/build.gradle.kts index fb01a6636..b80525bb1 100644 --- a/instrumentation/okhttp/okhttp-3.0/library/build.gradle.kts +++ b/instrumentation/okhttp/okhttp-3.0/library/build.gradle.kts @@ -16,5 +16,6 @@ android { dependencies { compileOnly(libs.okhttp) api(libs.opentelemetry.instrumentation.okhttp) + api(project(":core")) implementation(libs.opentelemetry.instrumentation.apiSemconv) } diff --git a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/OkHttpInstrumentationConfig.java b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/OkHttpInstrumentation.java similarity index 65% rename from instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/OkHttpInstrumentationConfig.java rename to instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/OkHttpInstrumentation.java index d2138cce0..2e994b3b4 100644 --- a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/OkHttpInstrumentationConfig.java +++ b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/OkHttpInstrumentation.java @@ -5,24 +5,29 @@ package io.opentelemetry.instrumentation.library.okhttp.v3_0; +import android.app.Application; +import com.google.auto.service.AutoService; +import io.opentelemetry.android.OpenTelemetryRum; +import io.opentelemetry.android.instrumentation.AndroidInstrumentation; import io.opentelemetry.instrumentation.api.incubator.semconv.net.PeerServiceResolver; import io.opentelemetry.instrumentation.api.internal.HttpConstants; +import io.opentelemetry.instrumentation.library.okhttp.v3_0.internal.OkHttp3Singletons; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import org.jetbrains.annotations.NotNull; -/** Configuration for automatic instrumentation of okhttp requests. */ -public final class OkHttpInstrumentationConfig { - private static List capturedRequestHeaders = new ArrayList<>(); - private static List capturedResponseHeaders = new ArrayList<>(); - private static Set knownMethods = HttpConstants.KNOWN_METHODS; - private static Map peerServiceMapping = new HashMap<>(); - private static boolean emitExperimentalHttpClientMetrics; - - private OkHttpInstrumentationConfig() {} +/** Instrumentation for okhttp requests. */ +@AutoService(AndroidInstrumentation.class) +public class OkHttpInstrumentation implements AndroidInstrumentation { + private List capturedRequestHeaders = new ArrayList<>(); + private List capturedResponseHeaders = new ArrayList<>(); + private Set knownMethods = HttpConstants.KNOWN_METHODS; + private Map peerServiceMapping = new HashMap<>(); + private boolean emitExperimentalHttpClientMetrics; /** * Configures the HTTP request headers that will be captured as span attributes as described in @@ -36,11 +41,11 @@ private OkHttpInstrumentationConfig() {} * * @param requestHeaders A list of HTTP header names. */ - public static void setCapturedRequestHeaders(List requestHeaders) { - OkHttpInstrumentationConfig.capturedRequestHeaders = new ArrayList<>(requestHeaders); + public void setCapturedRequestHeaders(List requestHeaders) { + capturedRequestHeaders = new ArrayList<>(requestHeaders); } - public static List getCapturedRequestHeaders() { + public List getCapturedRequestHeaders() { return capturedRequestHeaders; } @@ -56,11 +61,11 @@ public static List getCapturedRequestHeaders() { * * @param responseHeaders A list of HTTP header names. */ - public static void setCapturedResponseHeaders(List responseHeaders) { - OkHttpInstrumentationConfig.capturedResponseHeaders = new ArrayList<>(responseHeaders); + public void setCapturedResponseHeaders(List responseHeaders) { + capturedResponseHeaders = new ArrayList<>(responseHeaders); } - public static List getCapturedResponseHeaders() { + public List getCapturedResponseHeaders() { return capturedResponseHeaders; } @@ -79,11 +84,11 @@ public static List getCapturedResponseHeaders() { * * @param knownMethods A set of recognized HTTP request methods. */ - public static void setKnownMethods(Set knownMethods) { - OkHttpInstrumentationConfig.knownMethods = new HashSet<>(knownMethods); + public void setKnownMethods(Set knownMethods) { + this.knownMethods = new HashSet<>(knownMethods); } - public static Set getKnownMethods() { + public Set getKnownMethods() { return knownMethods; } @@ -92,11 +97,11 @@ public static Set getKnownMethods() { * href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/span-general.md#general-remote-service-attributes">the * specification. */ - public static void setPeerServiceMapping(Map peerServiceMapping) { - OkHttpInstrumentationConfig.peerServiceMapping = new HashMap<>(peerServiceMapping); + public void setPeerServiceMapping(Map peerServiceMapping) { + this.peerServiceMapping = new HashMap<>(peerServiceMapping); } - public static PeerServiceResolver newPeerServiceResolver() { + public PeerServiceResolver newPeerServiceResolver() { return PeerServiceResolver.create(peerServiceMapping); } @@ -109,13 +114,17 @@ public static PeerServiceResolver newPeerServiceResolver() { * href="https://github.com/open-telemetry/semantic-conventions/blob/main/specification/metrics/semantic_conventions/http-metrics.md#metric-httpclientresponsesize"> * the response size. */ - public static void setEmitExperimentalHttpClientMetrics( - boolean emitExperimentalHttpClientMetrics) { - OkHttpInstrumentationConfig.emitExperimentalHttpClientMetrics = - emitExperimentalHttpClientMetrics; + public void setEmitExperimentalHttpClientMetrics(boolean emitExperimentalHttpClientMetrics) { + this.emitExperimentalHttpClientMetrics = emitExperimentalHttpClientMetrics; } - public static boolean emitExperimentalHttpClientMetrics() { + public boolean emitExperimentalHttpClientMetrics() { return emitExperimentalHttpClientMetrics; } + + @Override + public void install( + @NotNull Application application, @NotNull OpenTelemetryRum openTelemetryRum) { + OkHttp3Singletons.configure(this, openTelemetryRum.getOpenTelemetry()); + } } diff --git a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/internal/CachedSupplier.java b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/internal/CachedSupplier.java deleted file mode 100644 index 33e1a8cae..000000000 --- a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/internal/CachedSupplier.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.library.okhttp.v3_0.internal; - -import java.util.function.Supplier; - -/** - * This class is internal and is hence not for public use. Its APIs are unstable and can change at - * any time. - */ -public class CachedSupplier implements Supplier { - private Supplier supplier; - private T instance; - private final Object lock = new Object(); - - public static CachedSupplier create(Supplier instance) { - return new CachedSupplier<>(instance); - } - - private CachedSupplier(Supplier supplier) { - this.supplier = supplier; - } - - @Override - public T get() { - synchronized (lock) { - if (instance == null) { - instance = supplier.get(); - if (instance == null) { - throw new NullPointerException("Supplier provided null."); - } - supplier = null; - } - return instance; - } - } -} diff --git a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/internal/LazyInterceptor.java b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/internal/LazyInterceptor.java deleted file mode 100644 index 0e7ea0484..000000000 --- a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/internal/LazyInterceptor.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.library.okhttp.v3_0.internal; - -import java.io.IOException; -import okhttp3.Interceptor; -import okhttp3.Response; -import org.jetbrains.annotations.NotNull; - -/** - * This class is internal and is hence not for public use. Its APIs are unstable and can change at - * any time. - */ -public class LazyInterceptor implements Interceptor { - private final CachedSupplier interceptorSupplier; - - public LazyInterceptor(CachedSupplier interceptorSupplier) { - this.interceptorSupplier = interceptorSupplier; - } - - @NotNull - @Override - public Response intercept(@NotNull Chain chain) throws IOException { - return interceptorSupplier.get().intercept(chain); - } -} diff --git a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/internal/OkHttp3Singletons.java b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/internal/OkHttp3Singletons.java index 862d21d9e..3d9f4c493 100644 --- a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/internal/OkHttp3Singletons.java +++ b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/internal/OkHttp3Singletons.java @@ -5,19 +5,18 @@ package io.opentelemetry.instrumentation.library.okhttp.v3_0.internal; -import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.incubator.semconv.net.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.semconv.http.HttpClientRequestResendCount; import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractor; -import io.opentelemetry.instrumentation.library.okhttp.v3_0.OkHttpInstrumentationConfig; +import io.opentelemetry.instrumentation.library.okhttp.v3_0.OkHttpInstrumentation; import io.opentelemetry.instrumentation.okhttp.v3_0.internal.ConnectionErrorSpanInterceptor; import io.opentelemetry.instrumentation.okhttp.v3_0.internal.OkHttpAttributesGetter; import io.opentelemetry.instrumentation.okhttp.v3_0.internal.OkHttpClientInstrumenterBuilderFactory; import io.opentelemetry.instrumentation.okhttp.v3_0.internal.TracingInterceptor; -import java.util.function.Supplier; import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; @@ -27,37 +26,36 @@ * any time. */ public final class OkHttp3Singletons { + private static final Interceptor NOOP_INTERCEPTOR = (chain -> chain.proceed(chain.request())); + public static Interceptor CONNECTION_ERROR_INTERCEPTOR = NOOP_INTERCEPTOR; + public static Interceptor TRACING_INTERCEPTOR = NOOP_INTERCEPTOR; - private static final Supplier> INSTRUMENTER = - CachedSupplier.create( - () -> - OkHttpClientInstrumenterBuilderFactory.create(GlobalOpenTelemetry.get()) - .setCapturedRequestHeaders( - OkHttpInstrumentationConfig.getCapturedRequestHeaders()) - .setCapturedResponseHeaders( - OkHttpInstrumentationConfig - .getCapturedResponseHeaders()) - .setKnownMethods(OkHttpInstrumentationConfig.getKnownMethods()) - // TODO: Do we really need to set the known methods on the span - // name - // extractor as well? - .setSpanNameExtractor( - x -> - HttpSpanNameExtractor.builder( - OkHttpAttributesGetter.INSTANCE) - .setKnownMethods( - OkHttpInstrumentationConfig - .getKnownMethods()) - .build()) - .addAttributeExtractor( - PeerServiceAttributesExtractor.create( - OkHttpAttributesGetter.INSTANCE, - OkHttpInstrumentationConfig - .newPeerServiceResolver())) - .setEmitExperimentalHttpClientMetrics( - OkHttpInstrumentationConfig - .emitExperimentalHttpClientMetrics()) - .build()); + public static void configure( + OkHttpInstrumentation instrumentation, OpenTelemetry openTelemetry) { + Instrumenter instrumenter = + OkHttpClientInstrumenterBuilderFactory.create(openTelemetry) + .setCapturedRequestHeaders(instrumentation.getCapturedRequestHeaders()) + .setCapturedResponseHeaders(instrumentation.getCapturedResponseHeaders()) + .setKnownMethods(instrumentation.getKnownMethods()) + // TODO: Do we really need to set the known methods on the span + // name + // extractor as well? + .setSpanNameExtractor( + x -> + HttpSpanNameExtractor.builder( + OkHttpAttributesGetter.INSTANCE) + .setKnownMethods(instrumentation.getKnownMethods()) + .build()) + .addAttributeExtractor( + PeerServiceAttributesExtractor.create( + OkHttpAttributesGetter.INSTANCE, + instrumentation.newPeerServiceResolver())) + .setEmitExperimentalHttpClientMetrics( + instrumentation.emitExperimentalHttpClientMetrics()) + .build(); + CONNECTION_ERROR_INTERCEPTOR = new ConnectionErrorSpanInterceptor(instrumenter); + TRACING_INTERCEPTOR = new TracingInterceptor(instrumenter, openTelemetry.getPropagators()); + } public static final Interceptor CALLBACK_CONTEXT_INTERCEPTOR = chain -> { @@ -81,18 +79,5 @@ public final class OkHttp3Singletons { } }; - public static final Interceptor CONNECTION_ERROR_INTERCEPTOR = - new LazyInterceptor<>( - CachedSupplier.create( - () -> new ConnectionErrorSpanInterceptor(INSTRUMENTER.get()))); - - public static final Interceptor TRACING_INTERCEPTOR = - new LazyInterceptor<>( - CachedSupplier.create( - () -> - new TracingInterceptor( - INSTRUMENTER.get(), - GlobalOpenTelemetry.getPropagators()))); - private OkHttp3Singletons() {} } diff --git a/instrumentation/okhttp/okhttp-3.0/library/src/test/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/OkHttpInstrumentationConfigTest.java b/instrumentation/okhttp/okhttp-3.0/library/src/test/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/OkHttpInstrumentationTest.java similarity index 63% rename from instrumentation/okhttp/okhttp-3.0/library/src/test/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/OkHttpInstrumentationConfigTest.java rename to instrumentation/okhttp/okhttp-3.0/library/src/test/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/OkHttpInstrumentationTest.java index 5df479091..ed8a7a70f 100644 --- a/instrumentation/okhttp/okhttp-3.0/library/src/test/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/OkHttpInstrumentationConfigTest.java +++ b/instrumentation/okhttp/okhttp-3.0/library/src/test/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/OkHttpInstrumentationTest.java @@ -7,12 +7,20 @@ import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -public class OkHttpInstrumentationConfigTest { +class OkHttpInstrumentationTest { + private OkHttpInstrumentation instrumentation; + + @BeforeEach + void setUp() { + instrumentation = new OkHttpInstrumentation(); + } + @Test void validateDefaultHttpMethods() { - assertThat(OkHttpInstrumentationConfig.getKnownMethods()) + assertThat(instrumentation.getKnownMethods()) .containsExactlyInAnyOrder( "CONNECT", "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "TRACE"); diff --git a/instrumentation/okhttp/okhttp-3.0/library/src/test/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/internal/CachedSupplierTest.java b/instrumentation/okhttp/okhttp-3.0/library/src/test/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/internal/CachedSupplierTest.java deleted file mode 100644 index 44d7033a1..000000000 --- a/instrumentation/okhttp/okhttp-3.0/library/src/test/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/internal/CachedSupplierTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.library.okhttp.v3_0.internal; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -import java.util.function.Supplier; -import org.junit.jupiter.api.Test; - -class CachedSupplierTest { - - @Test - void provideAndCacheSuppliersResult() { - Supplier original = - spy( - new Supplier() { - @Override - public String get() { - return "Hello World"; - } - }); - CachedSupplier cached = CachedSupplier.create(original); - - for (int i = 0; i < 3; i++) { - assertThat(cached.get()).isEqualTo("Hello World"); - } - verify(original).get(); - } - - @Test - void validateSupplierDoesNotReturnNull() { - Supplier original = () -> null; - CachedSupplier cached = CachedSupplier.create(original); - - try { - cached.get(); - fail(); - } catch (NullPointerException ignored) { - } - } -} diff --git a/instrumentation/okhttp/okhttp-3.0/testing/src/androidTest/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/InstrumentationTest.java b/instrumentation/okhttp/okhttp-3.0/testing/src/androidTest/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/InstrumentationTest.java index d50d015a4..4100b507b 100644 --- a/instrumentation/okhttp/okhttp-3.0/testing/src/androidTest/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/InstrumentationTest.java +++ b/instrumentation/okhttp/okhttp-3.0/testing/src/androidTest/java/io/opentelemetry/instrumentation/library/okhttp/v3_0/InstrumentationTest.java @@ -8,12 +8,15 @@ import static org.junit.Assert.assertEquals; import androidx.annotation.NonNull; -import io.opentelemetry.android.test.common.OpenTelemetryTestUtils; +import io.opentelemetry.android.test.common.OpenTelemetryRumRule; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.context.Scope; import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; -import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; import java.io.IOException; import java.util.concurrent.CountDownLatch; import okhttp3.Call; @@ -25,11 +28,13 @@ import okhttp3.mockwebserver.MockWebServer; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; public class InstrumentationTest { private MockWebServer server; - private static final InMemorySpanExporter inMemorySpanExporter = InMemorySpanExporter.create(); + + @Rule public OpenTelemetryRumRule openTelemetryRumRule = new OpenTelemetryRumRule(); @Before public void setUp() throws IOException { @@ -40,15 +45,13 @@ public void setUp() throws IOException { @After public void tearDown() throws IOException { server.shutdown(); - inMemorySpanExporter.reset(); } @Test public void okhttpTraces() throws IOException { - OpenTelemetryTestUtils.setUpSpanExporter(inMemorySpanExporter); server.enqueue(new MockResponse().setResponseCode(200)); - Span span = OpenTelemetryTestUtils.getSpan(); + Span span = openTelemetryRumRule.getSpan(); try (Scope ignored = span.makeCurrent()) { OkHttpClient client = @@ -67,14 +70,13 @@ public void okhttpTraces() throws IOException { span.end(); - assertEquals(2, inMemorySpanExporter.getFinishedSpanItems().size()); + assertEquals(2, openTelemetryRumRule.inMemorySpanExporter.getFinishedSpanItems().size()); } @Test public void okhttpTraces_with_callback() throws InterruptedException { - OpenTelemetryTestUtils.setUpSpanExporter(inMemorySpanExporter); CountDownLatch lock = new CountDownLatch(1); - Span span = OpenTelemetryTestUtils.getSpan(); + Span span = openTelemetryRumRule.getSpan(); try (Scope ignored = span.makeCurrent()) { server.enqueue(new MockResponse().setResponseCode(200)); @@ -111,7 +113,7 @@ public void onResponse( lock.await(); span.end(); - assertEquals(2, inMemorySpanExporter.getFinishedSpanItems().size()); + assertEquals(2, openTelemetryRumRule.inMemorySpanExporter.getFinishedSpanItems().size()); } @Test @@ -121,13 +123,24 @@ public void avoidCreatingSpansForInternalOkhttpRequests() throws InterruptedExce // so it should be run isolated to actually get it to fail when it's expected to fail. OtlpHttpSpanExporter exporter = OtlpHttpSpanExporter.builder().setEndpoint(server.url("").toString()).build(); - OpenTelemetryTestUtils.setUpSpanExporter(exporter); + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder() + .setTracerProvider( + SdkTracerProvider.builder() + .addSpanProcessor(SimpleSpanProcessor.create(exporter)) + .build()) + .build(); server.enqueue(new MockResponse().setResponseCode(200)); // This span should trigger 1 export okhttp call, which is the only okhttp call expected // for this test case. - OpenTelemetryTestUtils.getSpan().end(); + openTelemetry + .tracerBuilder("Some Scope") + .build() + .spanBuilder("Some Span") + .startSpan() + .end(); // Wait for unwanted extra okhttp requests. int loop = 0; diff --git a/test-common/build.gradle.kts b/test-common/build.gradle.kts index dd41ad0aa..04573480e 100644 --- a/test-common/build.gradle.kts +++ b/test-common/build.gradle.kts @@ -9,8 +9,11 @@ android { } dependencies { + api(project(":core")) api(platform(libs.opentelemetry.platform)) api(libs.opentelemetry.sdk) api(libs.opentelemetry.api) + api(libs.opentelemetry.sdk.testing) implementation(libs.androidx.core) + implementation(libs.androidx.junit) } diff --git a/test-common/src/main/kotlin/io/opentelemetry/android/test/common/OpenTelemetryRumRule.kt b/test-common/src/main/kotlin/io/opentelemetry/android/test/common/OpenTelemetryRumRule.kt new file mode 100644 index 000000000..ec648e6cd --- /dev/null +++ b/test-common/src/main/kotlin/io/opentelemetry/android/test/common/OpenTelemetryRumRule.kt @@ -0,0 +1,56 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.android.test.common + +import androidx.test.core.app.ApplicationProvider +import androidx.test.platform.app.InstrumentationRegistry +import io.opentelemetry.android.OpenTelemetryRum +import io.opentelemetry.api.trace.Span +import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor +import org.junit.rules.TestRule +import org.junit.runner.Description +import org.junit.runners.model.Statement + +/** + * Intended for Android Instrumentation tests only. + * + * This rule initializes a [OpenTelemetryRum] on every test and configures it to have + * in-memory exporters. + */ +class OpenTelemetryRumRule : TestRule { + lateinit var openTelemetryRum: OpenTelemetryRum + lateinit var inMemorySpanExporter: InMemorySpanExporter + + override fun apply( + base: Statement, + description: Description, + ): Statement { + return object : Statement() { + override fun evaluate() { + setUpOpenTelemetry() + base.evaluate() + } + } + } + + fun getSpan(): Span { + return openTelemetryRum.openTelemetry.getTracer("TestTracer").spanBuilder("A Span") + .startSpan() + } + + private fun setUpOpenTelemetry() { + inMemorySpanExporter = InMemorySpanExporter.create() + InstrumentationRegistry.getInstrumentation().runOnMainSync { + openTelemetryRum = + OpenTelemetryRum.builder(ApplicationProvider.getApplicationContext()) + .addTracerProviderCustomizer { tracer, _ -> + tracer.addSpanProcessor(SimpleSpanProcessor.create(inMemorySpanExporter)) + } + .build() + } + } +}