|
28 | 28 | import java.net.http.HttpRequest; |
29 | 29 | import java.net.http.HttpResponse; |
30 | 30 | import java.util.concurrent.CompletableFuture; |
| 31 | +import javax.annotation.Nullable; |
31 | 32 | import net.bytebuddy.asm.Advice; |
| 33 | +import net.bytebuddy.asm.Advice.AssignReturned; |
32 | 34 | import net.bytebuddy.description.type.TypeDescription; |
33 | 35 | import net.bytebuddy.matcher.ElementMatcher; |
34 | 36 |
|
@@ -65,87 +67,114 @@ public void transform(TypeTransformer transformer) { |
65 | 67 | HttpClientInstrumentation.class.getName() + "$SendAsyncAdvice"); |
66 | 68 | } |
67 | 69 |
|
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 | + } |
70 | 82 |
|
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) { |
76 | 89 | 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; |
79 | 121 | } |
| 122 | + return true; |
| 123 | + } |
80 | 124 |
|
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); |
83 | 139 | } |
84 | 140 |
|
85 | 141 | @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) |
86 | 142 | public static void methodExit( |
87 | 143 | @Advice.Argument(0) HttpRequest httpRequest, |
88 | 144 | @Advice.Return HttpResponse<?> httpResponse, |
89 | 145 | @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) { |
95 | 147 |
|
96 | | - scope.close(); |
97 | | - instrumenter().end(context, httpRequest, httpResponse, throwable); |
| 148 | + if (locals != null) { |
| 149 | + locals.end(httpRequest, httpResponse, throwable); |
| 150 | + } |
98 | 151 | } |
99 | 152 | } |
100 | 153 |
|
101 | 154 | @SuppressWarnings("unused") |
102 | 155 | public static class SendAsyncAdvice { |
103 | 156 |
|
104 | 157 | @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); |
123 | 160 | } |
124 | 161 |
|
| 162 | + @AssignReturned.ToReturned |
125 | 163 | @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) |
126 | | - public static void methodExit( |
| 164 | + public static CompletableFuture<HttpResponse<?>> methodExit( |
127 | 165 | @Advice.Argument(value = 0) HttpRequest httpRequest, |
128 | | - @Advice.Return(readOnly = false) CompletableFuture<HttpResponse<?>> future, |
| 166 | + @Advice.Return CompletableFuture<HttpResponse<?>> originalFuture, |
129 | 167 | @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) { |
137 | 169 |
|
138 | | - if (scope == null) { |
139 | | - return; |
140 | | - } |
| 170 | + @SuppressWarnings("UnnecessaryLocalVariable") |
| 171 | + CompletableFuture<HttpResponse<?>> future = originalFuture; |
141 | 172 |
|
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; |
148 | 175 | } |
| 176 | + |
| 177 | + return locals.wrapFuture(future, httpRequest); |
149 | 178 | } |
150 | 179 | } |
151 | 180 | } |
0 commit comments