Skip to content

Commit

Permalink
OkHttp migration to instrumentaion API (#505)
Browse files Browse the repository at this point in the history
* Removing OkHttpInstrumentationConfig statics

* Renaming OkHttpInstrumentationConfig

* Using okhttp instrumentation from registry

* Validating install

* Updating okhttp instrumentation redme

* Adding autoservice annotation to OkHttpInstrumentation

* Removing GlobalOpenTelemetry references from okhttp instrumentation related code

* Configuring OkHttp3Singletons from within OkHttpInstrumentation

* Clean up

* Initializing okhttp initial interceptors to noop

* Setting up convention for instrumentation tests

* Fixing okhttp tests

* Clean up

* Updating the README

* Fixing typo
  • Loading branch information
LikeTheSalad authored Aug 14, 2024
1 parent 5ef964e commit 64f42d7
Show file tree
Hide file tree
Showing 13 changed files with 183 additions and 208 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ android {

val libs = extensions.getByType<VersionCatalogsExtension>().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())
Expand Down
11 changes: 6 additions & 5 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand All @@ -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" }
Expand Down
15 changes: 14 additions & 1 deletion instrumentation/okhttp/okhttp-3.0/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
1 change: 1 addition & 0 deletions instrumentation/okhttp/okhttp-3.0/library/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ android {
dependencies {
compileOnly(libs.okhttp)
api(libs.opentelemetry.instrumentation.okhttp)
api(project(":core"))
implementation(libs.opentelemetry.instrumentation.apiSemconv)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> capturedRequestHeaders = new ArrayList<>();
private static List<String> capturedResponseHeaders = new ArrayList<>();
private static Set<String> knownMethods = HttpConstants.KNOWN_METHODS;
private static Map<String, String> peerServiceMapping = new HashMap<>();
private static boolean emitExperimentalHttpClientMetrics;

private OkHttpInstrumentationConfig() {}
/** Instrumentation for okhttp requests. */
@AutoService(AndroidInstrumentation.class)
public class OkHttpInstrumentation implements AndroidInstrumentation {
private List<String> capturedRequestHeaders = new ArrayList<>();
private List<String> capturedResponseHeaders = new ArrayList<>();
private Set<String> knownMethods = HttpConstants.KNOWN_METHODS;
private Map<String, String> peerServiceMapping = new HashMap<>();
private boolean emitExperimentalHttpClientMetrics;

/**
* Configures the HTTP request headers that will be captured as span attributes as described in
Expand All @@ -36,11 +41,11 @@ private OkHttpInstrumentationConfig() {}
*
* @param requestHeaders A list of HTTP header names.
*/
public static void setCapturedRequestHeaders(List<String> requestHeaders) {
OkHttpInstrumentationConfig.capturedRequestHeaders = new ArrayList<>(requestHeaders);
public void setCapturedRequestHeaders(List<String> requestHeaders) {
capturedRequestHeaders = new ArrayList<>(requestHeaders);
}

public static List<String> getCapturedRequestHeaders() {
public List<String> getCapturedRequestHeaders() {
return capturedRequestHeaders;
}

Expand All @@ -56,11 +61,11 @@ public static List<String> getCapturedRequestHeaders() {
*
* @param responseHeaders A list of HTTP header names.
*/
public static void setCapturedResponseHeaders(List<String> responseHeaders) {
OkHttpInstrumentationConfig.capturedResponseHeaders = new ArrayList<>(responseHeaders);
public void setCapturedResponseHeaders(List<String> responseHeaders) {
capturedResponseHeaders = new ArrayList<>(responseHeaders);
}

public static List<String> getCapturedResponseHeaders() {
public List<String> getCapturedResponseHeaders() {
return capturedResponseHeaders;
}

Expand All @@ -79,11 +84,11 @@ public static List<String> getCapturedResponseHeaders() {
*
* @param knownMethods A set of recognized HTTP request methods.
*/
public static void setKnownMethods(Set<String> knownMethods) {
OkHttpInstrumentationConfig.knownMethods = new HashSet<>(knownMethods);
public void setKnownMethods(Set<String> knownMethods) {
this.knownMethods = new HashSet<>(knownMethods);
}

public static Set<String> getKnownMethods() {
public Set<String> getKnownMethods() {
return knownMethods;
}

Expand All @@ -92,11 +97,11 @@ public static Set<String> getKnownMethods() {
* href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/span-general.md#general-remote-service-attributes">the
* specification</a>.
*/
public static void setPeerServiceMapping(Map<String, String> peerServiceMapping) {
OkHttpInstrumentationConfig.peerServiceMapping = new HashMap<>(peerServiceMapping);
public void setPeerServiceMapping(Map<String, String> peerServiceMapping) {
this.peerServiceMapping = new HashMap<>(peerServiceMapping);
}

public static PeerServiceResolver newPeerServiceResolver() {
public PeerServiceResolver newPeerServiceResolver() {
return PeerServiceResolver.create(peerServiceMapping);
}

Expand All @@ -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</a>.
*/
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());
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<Request, Response>> 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<Request, Response> 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 -> {
Expand All @@ -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() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Loading

0 comments on commit 64f42d7

Please sign in to comment.