-
Notifications
You must be signed in to change notification settings - Fork 871
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Concurrent http client tests with connection reuse (#2550)
* Concurrent http client tests with connection reuse * Scope exception handler callback
- Loading branch information
Showing
15 changed files
with
454 additions
and
117 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 0 additions & 90 deletions
90
...ain/java/io/opentelemetry/javaagent/instrumentation/vertx/HttpRequestInstrumentation.java
This file was deleted.
Oops, something went wrong.
21 changes: 0 additions & 21 deletions
21
...x-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/VertxTracer.java
This file was deleted.
Oops, something went wrong.
18 changes: 18 additions & 0 deletions
18
...b-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/client/Contexts.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.vertx.client; | ||
|
||
import io.opentelemetry.context.Context; | ||
|
||
public class Contexts { | ||
public final Context parentContext; | ||
public final Context context; | ||
|
||
public Contexts(Context parentContext, Context context) { | ||
this.parentContext = parentContext; | ||
this.context = context; | ||
} | ||
} |
177 changes: 177 additions & 0 deletions
177
...a/io/opentelemetry/javaagent/instrumentation/vertx/client/HttpRequestInstrumentation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.vertx.client; | ||
|
||
import static io.opentelemetry.javaagent.instrumentation.vertx.client.VertxClientTracer.tracer; | ||
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.implementsInterface; | ||
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.ClassLoaderMatcher.hasClassesNamed; | ||
import static net.bytebuddy.matcher.ElementMatchers.isMethod; | ||
import static net.bytebuddy.matcher.ElementMatchers.isPrivate; | ||
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; | ||
import static net.bytebuddy.matcher.ElementMatchers.named; | ||
|
||
import io.opentelemetry.context.Context; | ||
import io.opentelemetry.context.Scope; | ||
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext; | ||
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge; | ||
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; | ||
import io.vertx.core.http.HttpClientRequest; | ||
import io.vertx.core.http.HttpClientResponse; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import net.bytebuddy.asm.Advice; | ||
import net.bytebuddy.description.method.MethodDescription; | ||
import net.bytebuddy.description.type.TypeDescription; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
|
||
/** | ||
* Two things happen in this instrumentation. | ||
* | ||
* <p>First, {@link EndRequestAdvice}, {@link HandleExceptionAdvice} and {@link | ||
* HandleResponseAdvice} deal with the common start span/end span functionality. As Vert.x is async | ||
* framework, calls to the instrumented methods may happen from different threads. Thus, correct | ||
* context is stored in {@code HttpClientRequest} itself. | ||
* | ||
* <p>Second, when HttpClientRequest calls any method that actually performs write on the underlying | ||
* Netty channel, {@link MountContextAdvice} scopes that method call into the context captured on | ||
* the first step. This ensures proper context transfer between the client who actually initiated | ||
* the http call and the Netty Channel that will perform that operation. The main result of this | ||
* transfer is a suppression of Netty CLIENT span. | ||
*/ | ||
public class HttpRequestInstrumentation implements TypeInstrumentation { | ||
|
||
@Override | ||
public ElementMatcher<ClassLoader> classLoaderOptimization() { | ||
return hasClassesNamed("io.vertx.core.http.HttpClientRequest"); | ||
} | ||
|
||
@Override | ||
public ElementMatcher<TypeDescription> typeMatcher() { | ||
return implementsInterface(named("io.vertx.core.http.HttpClientRequest")); | ||
} | ||
|
||
@Override | ||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() { | ||
Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>(); | ||
|
||
transformers.put( | ||
isMethod().and(nameStartsWith("end").or(named("sendHead"))), | ||
HttpRequestInstrumentation.class.getName() + "$EndRequestAdvice"); | ||
|
||
transformers.put( | ||
isMethod().and(named("handleException")), | ||
HttpRequestInstrumentation.class.getName() + "$HandleExceptionAdvice"); | ||
|
||
transformers.put( | ||
isMethod().and(named("handleResponse")), | ||
HttpRequestInstrumentation.class.getName() + "$HandleResponseAdvice"); | ||
|
||
transformers.put( | ||
isMethod().and(isPrivate()).and(nameStartsWith("write").or(nameStartsWith("connected"))), | ||
HttpRequestInstrumentation.class.getName() + "$MountContextAdvice"); | ||
return transformers; | ||
} | ||
|
||
public static class EndRequestAdvice { | ||
@Advice.OnMethodEnter(suppress = Throwable.class) | ||
public static void attachContext( | ||
@Advice.This HttpClientRequest request, @Advice.Local("otelScope") Scope scope) { | ||
Context parentContext = Java8BytecodeBridge.currentContext(); | ||
|
||
if (!tracer().shouldStartSpan(parentContext)) { | ||
return; | ||
} | ||
|
||
Context context = tracer().startSpan(parentContext, request, request); | ||
Contexts contexts = new Contexts(parentContext, context); | ||
InstrumentationContext.get(HttpClientRequest.class, Contexts.class).put(request, contexts); | ||
|
||
scope = context.makeCurrent(); | ||
} | ||
|
||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) | ||
public static void endScope(@Advice.Local("otelScope") Scope scope) { | ||
if (scope != null) { | ||
scope.close(); | ||
} | ||
} | ||
} | ||
|
||
public static class HandleExceptionAdvice { | ||
@Advice.OnMethodEnter(suppress = Throwable.class) | ||
public static void handleException( | ||
@Advice.This HttpClientRequest request, | ||
@Advice.Argument(0) Throwable t, | ||
@Advice.Local("otelScope") Scope scope) { | ||
Contexts contexts = | ||
InstrumentationContext.get(HttpClientRequest.class, Contexts.class).get(request); | ||
|
||
if (contexts == null) { | ||
return; | ||
} | ||
|
||
tracer().endExceptionally(contexts.context, t); | ||
|
||
// Scoping all potential callbacks etc to the parent context | ||
scope = contexts.parentContext.makeCurrent(); | ||
} | ||
|
||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) | ||
public static void handleResponseExit(@Advice.Local("otelScope") Scope scope) { | ||
if (scope != null) { | ||
scope.close(); | ||
} | ||
} | ||
} | ||
|
||
public static class HandleResponseAdvice { | ||
@Advice.OnMethodEnter(suppress = Throwable.class) | ||
public static void handleResponseEnter( | ||
@Advice.This HttpClientRequest request, | ||
@Advice.Argument(0) HttpClientResponse response, | ||
@Advice.Local("otelScope") Scope scope) { | ||
Contexts contexts = | ||
InstrumentationContext.get(HttpClientRequest.class, Contexts.class).get(request); | ||
|
||
if (contexts == null) { | ||
return; | ||
} | ||
|
||
tracer().end(contexts.context, response); | ||
|
||
// Scoping all potential callbacks etc to the parent context | ||
scope = contexts.parentContext.makeCurrent(); | ||
} | ||
|
||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) | ||
public static void handleResponseExit(@Advice.Local("otelScope") Scope scope) { | ||
if (scope != null) { | ||
scope.close(); | ||
} | ||
} | ||
} | ||
|
||
public static class MountContextAdvice { | ||
@Advice.OnMethodEnter(suppress = Throwable.class) | ||
public static void mountContext( | ||
@Advice.This HttpClientRequest request, @Advice.Local("otelScope") Scope scope) { | ||
Contexts contexts = | ||
InstrumentationContext.get(HttpClientRequest.class, Contexts.class).get(request); | ||
if (contexts == null) { | ||
return; | ||
} | ||
|
||
scope = contexts.context.makeCurrent(); | ||
} | ||
|
||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) | ||
public static void unmountContext(@Advice.Local("otelScope") Scope scope) { | ||
if (scope != null) { | ||
scope.close(); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.