HTTP client instrumentation cleanup: http-url-connection (#1908)

* HttpClientTracer cleanup: http-url-connection

* Checkstyle

* Feedback
This commit is contained in:
Trask Stalnaker 2020-12-15 21:52:16 -08:00 committed by GitHub
parent 6dcc819f77
commit 9e38f521f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 47 deletions

View File

@ -20,6 +20,7 @@ import static net.bytebuddy.matcher.ElementMatchers.not;
import com.google.auto.service.AutoService;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
@ -38,7 +39,7 @@ import net.bytebuddy.matcher.ElementMatchers;
public class HttpUrlConnectionInstrumentationModule extends InstrumentationModule {
public HttpUrlConnectionInstrumentationModule() {
super("httpurlconnection");
super("http-url-connection");
}
@Override
@ -79,75 +80,82 @@ public class HttpUrlConnectionInstrumentationModule extends InstrumentationModul
public static class HttpUrlConnectionAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static HttpUrlState methodEnter(
public static void methodEnter(
@Advice.This HttpURLConnection connection,
@Advice.FieldValue("connected") boolean connected,
@Advice.Local("otelScope") Scope scope) {
@Advice.Local("otelHttpUrlState") HttpUrlState httpUrlState,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") CallDepth callDepth) {
int callDepth = CallDepthThreadLocalMap.incrementCallDepth(HttpURLConnection.class);
if (callDepth > 0) {
return null;
callDepth = CallDepthThreadLocalMap.getCallDepth(HttpURLConnection.class);
if (callDepth.getAndIncrement() > 0) {
// only want the rest of the instrumentation rules (which are complex enough) to apply to
// top-level HttpURLConnection calls
return;
}
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
ContextStore<HttpURLConnection, HttpUrlState> contextStore =
// using storage for a couple of reasons:
// - to start an operation in connect() and end it in getInputStream()
// - to avoid creating a new operation on multiple subsequent calls to getInputStream()
ContextStore<HttpURLConnection, HttpUrlState> storage =
InstrumentationContext.get(HttpURLConnection.class, HttpUrlState.class);
HttpUrlState state = contextStore.putIfAbsent(connection, HttpUrlState::new);
httpUrlState = storage.get(connection);
synchronized (state) {
if (!state.initialized) {
Context parentContext = currentContext();
if (tracer().shouldStartSpan(parentContext)) {
state.context = tracer().startSpan(parentContext, connection, connection);
if (!connected) {
scope = state.context.makeCurrent();
}
}
state.initialized = true;
if (httpUrlState != null) {
if (!httpUrlState.finished) {
scope = httpUrlState.context.makeCurrent();
}
return;
}
return state;
Context context = tracer().startSpan(parentContext, connection);
httpUrlState = new HttpUrlState(context);
storage.put(connection, httpUrlState);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Enter HttpUrlState state,
@Advice.This HttpURLConnection connection,
@Advice.FieldValue("responseCode") int responseCode,
@Advice.Thrown Throwable throwable,
@Advice.Origin("#m") String methodName,
@Advice.Local("otelScope") Scope scope) {
if (scope != null) {
scope.close();
}
if (state == null) {
@Advice.Local("otelHttpUrlState") HttpUrlState httpUrlState,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") CallDepth callDepth) {
if (callDepth.decrementAndGet() > 0) {
return;
}
CallDepthThreadLocalMap.reset(HttpURLConnection.class);
if (scope == null) {
return;
}
scope.close();
synchronized (state) {
if (state.context != null && !state.finished) {
if (throwable != null) {
tracer().endExceptionally(state.context, throwable);
state.finished = true;
} else if ("getInputStream".equals(methodName)) {
// responseCode field is sometimes not populated.
// We can't call getResponseCode() due to some unwanted side-effects
// (e.g. breaks getOutputStream).
if (responseCode > 0) {
tracer().end(state.context, new HttpUrlResponse(connection, responseCode));
state.finished = true;
}
}
}
if (throwable != null) {
tracer().endExceptionally(httpUrlState.context, throwable);
httpUrlState.finished = true;
} else if (methodName.equals("getInputStream") && responseCode > 0) {
// responseCode field is sometimes not populated.
// We can't call getResponseCode() due to some unwanted side-effects
// (e.g. breaks getOutputStream).
tracer().end(httpUrlState.context, new HttpUrlResponse(connection, responseCode));
httpUrlState.finished = true;
}
}
}
// state is always accessed under synchronized block
// everything is public since called directly from advice code
// (which is inlined into other packages)
public static class HttpUrlState {
public boolean initialized;
public Context context;
public final Context context;
public boolean finished;
public HttpUrlState(Context context) {
this.context = context;
}
}
}

View File

@ -7,7 +7,8 @@ package io.opentelemetry.javaagent.instrumentation.httpurlconnection;
import static io.opentelemetry.javaagent.instrumentation.httpurlconnection.HeadersInjectAdapter.SETTER;
import io.opentelemetry.context.propagation.TextMapPropagator.Setter;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
import java.net.HttpURLConnection;
import java.net.URI;
@ -22,6 +23,10 @@ public class HttpUrlConnectionTracer
return TRACER;
}
public Context startSpan(Context parentContext, HttpURLConnection request) {
return super.startSpan(parentContext, request, request);
}
@Override
protected String method(HttpURLConnection connection) {
return connection.getRequestMethod();
@ -48,7 +53,7 @@ public class HttpUrlConnectionTracer
}
@Override
protected Setter<HttpURLConnection> getSetter() {
protected TextMapPropagator.Setter<HttpURLConnection> getSetter() {
return SETTER;
}