Skip to content

Commit 04b3353

Browse files
committed
java-http-client
1 parent d1a18e1 commit 04b3353

File tree

3 files changed

+95
-57
lines changed

3 files changed

+95
-57
lines changed

instrumentation/java-http-client/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/javahttpclient/HttpClientInstrumentation.java

Lines changed: 83 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
import java.net.http.HttpRequest;
2929
import java.net.http.HttpResponse;
3030
import java.util.concurrent.CompletableFuture;
31+
import javax.annotation.Nullable;
3132
import net.bytebuddy.asm.Advice;
33+
import net.bytebuddy.asm.Advice.AssignReturned;
3234
import net.bytebuddy.description.type.TypeDescription;
3335
import net.bytebuddy.matcher.ElementMatcher;
3436

@@ -65,87 +67,114 @@ public void transform(TypeTransformer transformer) {
6567
HttpClientInstrumentation.class.getName() + "$SendAsyncAdvice");
6668
}
6769

68-
@SuppressWarnings("unused")
69-
public static class SendAdvice {
70+
public static class AdviceLocals {
71+
private final Context context;
72+
private final Context parentContext;
73+
private final Scope scope;
74+
private final CallDepth callDepth;
75+
76+
private AdviceLocals(Context parentContext, Context context, Scope scope, CallDepth callDepth) {
77+
this.parentContext = parentContext;
78+
this.context = context;
79+
this.scope = scope;
80+
this.callDepth = callDepth;
81+
}
7082

71-
@Advice.OnMethodEnter(suppress = Throwable.class)
72-
public static void methodEnter(
73-
@Advice.Argument(value = 0) HttpRequest httpRequest,
74-
@Advice.Local("otelContext") Context context,
75-
@Advice.Local("otelScope") Scope scope) {
83+
@Nullable
84+
public static AdviceLocals start(HttpRequest request) {
85+
return start(request, null);
86+
}
87+
88+
private static AdviceLocals start(HttpRequest request, CallDepth callDepth) {
7689
Context parentContext = currentContext();
77-
if (!instrumenter().shouldStart(parentContext, httpRequest)) {
78-
return;
90+
if (!instrumenter().shouldStart(parentContext, request)) {
91+
return null;
92+
}
93+
94+
Context context = instrumenter().start(parentContext, request);
95+
return new AdviceLocals(parentContext, context, context.makeCurrent(), callDepth);
96+
}
97+
98+
public void end(HttpRequest request, HttpResponse<?> response, Throwable throwable) {
99+
scope.close();
100+
instrumenter().end(context, request, response, throwable);
101+
}
102+
103+
public static AdviceLocals startWithCallDepth(HttpRequest httpRequest) {
104+
CallDepth callDepth = CallDepth.forClass(HttpClient.class);
105+
if (callDepth.getAndIncrement() > 0) {
106+
return new AdviceLocals(null, null, null, callDepth);
107+
}
108+
return start(httpRequest, callDepth);
109+
}
110+
111+
public boolean endWithCallDepth(HttpRequest httpRequest, Throwable throwable) {
112+
113+
if (callDepth.decrementAndGet() > 0) {
114+
return false;
115+
}
116+
117+
scope.close();
118+
if (throwable != null) {
119+
instrumenter().end(context, httpRequest, null, throwable);
120+
return false;
79121
}
122+
return true;
123+
}
80124

81-
context = instrumenter().start(parentContext, httpRequest);
82-
scope = context.makeCurrent();
125+
public CompletableFuture<HttpResponse<?>> wrapFuture(
126+
CompletableFuture<HttpResponse<?>> future, HttpRequest httpRequest) {
127+
future = future.whenComplete(new ResponseConsumer(instrumenter(), context, httpRequest));
128+
return CompletableFutureWrapper.wrap(future, parentContext);
129+
}
130+
}
131+
132+
@SuppressWarnings("unused")
133+
public static class SendAdvice {
134+
135+
@Nullable
136+
@Advice.OnMethodEnter(suppress = Throwable.class)
137+
public static AdviceLocals methodEnter(@Advice.Argument(value = 0) HttpRequest httpRequest) {
138+
return AdviceLocals.start(httpRequest);
83139
}
84140

85141
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
86142
public static void methodExit(
87143
@Advice.Argument(0) HttpRequest httpRequest,
88144
@Advice.Return HttpResponse<?> httpResponse,
89145
@Advice.Thrown Throwable throwable,
90-
@Advice.Local("otelContext") Context context,
91-
@Advice.Local("otelScope") Scope scope) {
92-
if (scope == null) {
93-
return;
94-
}
146+
@Advice.Enter @Nullable AdviceLocals locals) {
95147

96-
scope.close();
97-
instrumenter().end(context, httpRequest, httpResponse, throwable);
148+
if (locals != null) {
149+
locals.end(httpRequest, httpResponse, throwable);
150+
}
98151
}
99152
}
100153

101154
@SuppressWarnings("unused")
102155
public static class SendAsyncAdvice {
103156

104157
@Advice.OnMethodEnter(suppress = Throwable.class)
105-
public static void methodEnter(
106-
@Advice.Argument(value = 0) HttpRequest httpRequest,
107-
@Advice.Local("otelCallDepth") CallDepth callDepth,
108-
@Advice.Local("otelContext") Context context,
109-
@Advice.Local("otelParentContext") Context parentContext,
110-
@Advice.Local("otelScope") Scope scope) {
111-
callDepth = CallDepth.forClass(HttpClient.class);
112-
if (callDepth.getAndIncrement() > 0) {
113-
return;
114-
}
115-
116-
parentContext = currentContext();
117-
if (!instrumenter().shouldStart(parentContext, httpRequest)) {
118-
return;
119-
}
120-
121-
context = instrumenter().start(parentContext, httpRequest);
122-
scope = context.makeCurrent();
158+
public static AdviceLocals methodEnter(@Advice.Argument(value = 0) HttpRequest httpRequest) {
159+
return AdviceLocals.startWithCallDepth(httpRequest);
123160
}
124161

162+
@AssignReturned.ToReturned
125163
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
126-
public static void methodExit(
164+
public static CompletableFuture<HttpResponse<?>> methodExit(
127165
@Advice.Argument(value = 0) HttpRequest httpRequest,
128-
@Advice.Return(readOnly = false) CompletableFuture<HttpResponse<?>> future,
166+
@Advice.Return CompletableFuture<HttpResponse<?>> originalFuture,
129167
@Advice.Thrown Throwable throwable,
130-
@Advice.Local("otelCallDepth") CallDepth callDepth,
131-
@Advice.Local("otelContext") Context context,
132-
@Advice.Local("otelParentContext") Context parentContext,
133-
@Advice.Local("otelScope") Scope scope) {
134-
if (callDepth.decrementAndGet() > 0) {
135-
return;
136-
}
168+
@Advice.Enter AdviceLocals locals) {
137169

138-
if (scope == null) {
139-
return;
140-
}
170+
@SuppressWarnings("UnnecessaryLocalVariable")
171+
CompletableFuture<HttpResponse<?>> future = originalFuture;
141172

142-
scope.close();
143-
if (throwable != null) {
144-
instrumenter().end(context, httpRequest, null, throwable);
145-
} else {
146-
future = future.whenComplete(new ResponseConsumer(instrumenter(), context, httpRequest));
147-
future = CompletableFutureWrapper.wrap(future, parentContext);
173+
if (!locals.endWithCallDepth(httpRequest, throwable)) {
174+
return future;
148175
}
176+
177+
return locals.wrapFuture(future, httpRequest);
149178
}
150179
}
151180
}

instrumentation/java-http-client/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/javahttpclient/HttpClientInstrumentationModule.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
import com.google.auto.service.AutoService;
1111
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
1212
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
13+
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
1314
import java.util.List;
1415

1516
@AutoService(InstrumentationModule.class)
16-
public class HttpClientInstrumentationModule extends InstrumentationModule {
17+
public class HttpClientInstrumentationModule extends InstrumentationModule
18+
implements ExperimentalInstrumentationModule {
1719
public HttpClientInstrumentationModule() {
1820
super("java-http-client");
1921
}
@@ -22,4 +24,9 @@ public HttpClientInstrumentationModule() {
2224
public List<TypeInstrumentation> typeInstrumentations() {
2325
return asList(new HttpClientInstrumentation(), new HttpHeadersInstrumentation());
2426
}
27+
28+
@Override
29+
public boolean isIndyReady() {
30+
return true;
31+
}
2532
}

instrumentation/java-http-client/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/javahttpclient/HttpHeadersInstrumentation.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
1717
import java.net.http.HttpHeaders;
1818
import net.bytebuddy.asm.Advice;
19+
import net.bytebuddy.asm.Advice.AssignReturned;
1920
import net.bytebuddy.description.type.TypeDescription;
2021
import net.bytebuddy.matcher.ElementMatcher;
2122

@@ -38,9 +39,10 @@ public void transform(TypeTransformer transformer) {
3839
@SuppressWarnings("unused")
3940
public static class HeadersAdvice {
4041

42+
@AssignReturned.ToReturned
4143
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
42-
public static void methodExit(@Advice.Return(readOnly = false) HttpHeaders headers) {
43-
headers = setter().inject(headers, Context.current());
44+
public static HttpHeaders methodExit(@Advice.Return HttpHeaders headers) {
45+
return setter().inject(headers, Context.current());
4446
}
4547
}
4648
}

0 commit comments

Comments
 (0)