HTTP client instrumentation cleanup: http-url-connection (#1908)
* HttpClientTracer cleanup: http-url-connection * Checkstyle * Feedback
This commit is contained in:
parent
6dcc819f77
commit
9e38f521f8
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue