Skip to content

Commit b52c197

Browse files
committed
Merge branch 'main' of github.com:open-telemetry/opentelemetry-java-instrumentation into semconv-stable-code
2 parents b380442 + b9894ce commit b52c197

File tree

17 files changed

+719
-7
lines changed

17 files changed

+719
-7
lines changed

.fossa.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,9 @@ targets:
11141114
- type: gradle
11151115
path: ./
11161116
target: ':instrumentation:vertx:vertx-http-client:vertx-http-client-4.0:javaagent'
1117+
- type: gradle
1118+
path: ./
1119+
target: ':instrumentation:vertx:vertx-http-client:vertx-http-client-5.0:javaagent'
11171120
- type: gradle
11181121
path: ./
11191122
target: ':instrumentation:vertx:vertx-http-client:vertx-http-client-common:javaagent'

.github/graal-native-docker-compose.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
services:
22
mongodb:
3-
image: mongo:4.0
3+
image: mongo:4.2
44
ports:
55
- "27017:27017"
66

.github/workflows/auto-license-report.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
java-version-file: .java-version
2929

3030
- name: Set up gradle
31-
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
31+
uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
3232
with:
3333
cache-read-only: true
3434

.github/workflows/auto-spotless.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
java-version-file: .java-version
2929

3030
- name: Set up gradle
31-
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
31+
uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
3232
with:
3333
cache-read-only: true
3434

.github/workflows/auto-update-pull-request.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
pull-requests: write
1919
steps:
2020
- name: Download patch
21-
uses: actions/[email protected]
21+
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
2222
with:
2323
run-id: ${{ github.event.workflow_run.id }}
2424
path: ${{ runner.temp }}

instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ dependencies {
2121

2222
testInstrumentation(project(":instrumentation:netty:netty-4.1:javaagent"))
2323

24-
latestDepTestLibrary("io.vertx:vertx-core:4.+") // documented limitation, 5.x not supported yet
25-
latestDepTestLibrary("io.vertx:vertx-codegen:4.+") // documented limitation, 5.x not supported yet
24+
latestDepTestLibrary("io.vertx:vertx-core:4.+") // see vertx-http-client-5.0 module
25+
latestDepTestLibrary("io.vertx:vertx-codegen:4.+") // see vertx-http-client-5.0 module
2626
}

instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/VertxClientInstrumentationModule.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ public VertxClientInstrumentationModule() {
2525
@Override
2626
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
2727
// class removed in 4.0
28-
return not(hasClassesNamed("io.vertx.core.Starter"));
28+
return not(hasClassesNamed("io.vertx.core.Starter"))
29+
// class added in 5.0
30+
.and(not(hasClassesNamed("io.vertx.core.http.impl.HttpClientConnectionInternal")));
2931
}
3032

3133
@Override
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
plugins {
2+
id("otel.javaagent-instrumentation")
3+
}
4+
5+
muzzle {
6+
pass {
7+
group.set("io.vertx")
8+
module.set("vertx-core")
9+
versions.set("[5.0.0,)")
10+
assertInverse.set(true)
11+
}
12+
}
13+
14+
otelJava {
15+
minJavaVersionSupported.set(JavaVersion.VERSION_11)
16+
}
17+
18+
dependencies {
19+
library("io.vertx:vertx-core:5.0.0")
20+
21+
// vertx-codegen dependency is needed for Xlint's annotation checking
22+
library("io.vertx:vertx-codegen:5.0.0")
23+
24+
implementation(project(":instrumentation:vertx:vertx-http-client:vertx-http-client-common:javaagent"))
25+
26+
testInstrumentation(project(":instrumentation:netty:netty-4.1:javaagent"))
27+
testInstrumentation(project(":instrumentation:vertx:vertx-http-client:vertx-http-client-4.0:javaagent"))
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.vertx.v5_0.client;
7+
8+
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
9+
import static net.bytebuddy.matcher.ElementMatchers.named;
10+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
11+
12+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
13+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
14+
import io.vertx.core.http.impl.HttpClientRequestBase;
15+
import io.vertx.core.net.HostAndPort;
16+
import net.bytebuddy.asm.Advice;
17+
import net.bytebuddy.description.type.TypeDescription;
18+
import net.bytebuddy.matcher.ElementMatcher;
19+
20+
public class HttpClientRequestBaseInstrumentation implements TypeInstrumentation {
21+
22+
@Override
23+
public ElementMatcher<TypeDescription> typeMatcher() {
24+
return named("io.vertx.core.http.impl.HttpClientRequestBase");
25+
}
26+
27+
@Override
28+
public void transform(TypeTransformer transformer) {
29+
transformer.applyAdviceToMethod(
30+
isConstructor(), this.getClass().getName() + "$ConstructorAdvice");
31+
32+
transformer.applyAdviceToMethod(
33+
named("authority").and(takesArgument(0, named("io.vertx.core.net.HostAndPort"))),
34+
this.getClass().getName() + "$SetAuthorityAdvice");
35+
}
36+
37+
@SuppressWarnings("unused")
38+
public static class ConstructorAdvice {
39+
@Advice.OnMethodExit(suppress = Throwable.class)
40+
public static void onExit(
41+
@Advice.This HttpClientRequestBase request,
42+
@Advice.FieldValue("authority") HostAndPort authority) {
43+
VertxClientSingletons.setAuthority(request, authority);
44+
}
45+
}
46+
47+
@SuppressWarnings("unused")
48+
public static class SetAuthorityAdvice {
49+
@Advice.OnMethodExit(suppress = Throwable.class)
50+
public static void onExit(
51+
@Advice.This HttpClientRequestBase request, @Advice.Argument(0) HostAndPort authority) {
52+
VertxClientSingletons.setAuthority(request, authority);
53+
}
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.vertx.v5_0.client;
7+
8+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
9+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
10+
import static io.opentelemetry.javaagent.instrumentation.vertx.v5_0.client.VertxClientSingletons.instrumenter;
11+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
12+
import static net.bytebuddy.matcher.ElementMatchers.isPrivate;
13+
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
14+
import static net.bytebuddy.matcher.ElementMatchers.named;
15+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
16+
17+
import io.opentelemetry.context.Context;
18+
import io.opentelemetry.context.Scope;
19+
import io.opentelemetry.instrumentation.api.util.VirtualField;
20+
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
21+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
22+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
23+
import io.opentelemetry.javaagent.instrumentation.vertx.client.Contexts;
24+
import io.opentelemetry.javaagent.instrumentation.vertx.client.ExceptionHandlerWrapper;
25+
import io.vertx.core.Handler;
26+
import io.vertx.core.http.HttpClientRequest;
27+
import io.vertx.core.http.HttpClientResponse;
28+
import net.bytebuddy.asm.Advice;
29+
import net.bytebuddy.description.type.TypeDescription;
30+
import net.bytebuddy.matcher.ElementMatcher;
31+
32+
/**
33+
* Two things happen in this instrumentation.
34+
*
35+
* <p>First, {@link EndRequestAdvice}, {@link HandleExceptionAdvice} and {@link
36+
* HandleResponseAdvice} deal with the common start span/end span functionality. As Vert.x is async
37+
* framework, calls to the instrumented methods may happen from different threads. Thus, correct
38+
* context is stored in {@code HttpClientRequest} itself.
39+
*
40+
* <p>Second, when HttpClientRequest calls any method that actually performs write on the underlying
41+
* Netty channel, {@link MountContextAdvice} scopes that method call into the context captured on
42+
* the first step. This ensures proper context transfer between the client who actually initiated
43+
* the http call and the Netty Channel that will perform that operation. The main result of this
44+
* transfer is a suppression of Netty CLIENT span.
45+
*/
46+
public class HttpRequestInstrumentation implements TypeInstrumentation {
47+
48+
@Override
49+
public ElementMatcher<ClassLoader> classLoaderOptimization() {
50+
return hasClassesNamed("io.vertx.core.http.HttpClientRequest");
51+
}
52+
53+
@Override
54+
public ElementMatcher<TypeDescription> typeMatcher() {
55+
return implementsInterface(named("io.vertx.core.http.HttpClientRequest"));
56+
}
57+
58+
@Override
59+
public void transform(TypeTransformer transformer) {
60+
transformer.applyAdviceToMethod(
61+
isMethod().and(nameStartsWith("end").or(named("sendHead"))),
62+
HttpRequestInstrumentation.class.getName() + "$EndRequestAdvice");
63+
64+
transformer.applyAdviceToMethod(
65+
isMethod().and(named("handleException")),
66+
HttpRequestInstrumentation.class.getName() + "$HandleExceptionAdvice");
67+
68+
transformer.applyAdviceToMethod(
69+
isMethod()
70+
.and(named("handleResponse"))
71+
.and(takesArgument(1, named("io.vertx.core.http.HttpClientResponse"))),
72+
HttpRequestInstrumentation.class.getName() + "$HandleResponseAdvice");
73+
74+
transformer.applyAdviceToMethod(
75+
isMethod().and(isPrivate()).and(nameStartsWith("write").or(nameStartsWith("connected"))),
76+
HttpRequestInstrumentation.class.getName() + "$MountContextAdvice");
77+
78+
transformer.applyAdviceToMethod(
79+
isMethod()
80+
.and(named("exceptionHandler"))
81+
.and(takesArgument(0, named("io.vertx.core.Handler"))),
82+
HttpRequestInstrumentation.class.getName() + "$ExceptionHandlerAdvice");
83+
}
84+
85+
@SuppressWarnings("unused")
86+
public static class EndRequestAdvice {
87+
88+
@Advice.OnMethodEnter(suppress = Throwable.class)
89+
public static void attachContext(
90+
@Advice.This HttpClientRequest request,
91+
@Advice.Local("otelContext") Context context,
92+
@Advice.Local("otelScope") Scope scope) {
93+
Context parentContext = Java8BytecodeBridge.currentContext();
94+
95+
if (!instrumenter().shouldStart(parentContext, request)) {
96+
return;
97+
}
98+
99+
context = instrumenter().start(parentContext, request);
100+
Contexts contexts = new Contexts(parentContext, context);
101+
VirtualField.find(HttpClientRequest.class, Contexts.class).set(request, contexts);
102+
103+
scope = context.makeCurrent();
104+
}
105+
106+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
107+
public static void endScope(
108+
@Advice.This HttpClientRequest request,
109+
@Advice.Local("otelContext") Context context,
110+
@Advice.Local("otelScope") Scope scope,
111+
@Advice.Thrown Throwable throwable) {
112+
if (scope != null) {
113+
scope.close();
114+
}
115+
if (throwable != null) {
116+
instrumenter().end(context, request, null, throwable);
117+
}
118+
}
119+
}
120+
121+
@SuppressWarnings("unused")
122+
public static class HandleExceptionAdvice {
123+
124+
@Advice.OnMethodEnter(suppress = Throwable.class)
125+
public static void handleException(
126+
@Advice.This HttpClientRequest request,
127+
@Advice.Argument(0) Throwable t,
128+
@Advice.Local("otelScope") Scope scope) {
129+
Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request);
130+
131+
if (contexts == null) {
132+
return;
133+
}
134+
135+
instrumenter().end(contexts.context, request, null, t);
136+
137+
// Scoping all potential callbacks etc to the parent context
138+
scope = contexts.parentContext.makeCurrent();
139+
}
140+
141+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
142+
public static void handleResponseExit(@Advice.Local("otelScope") Scope scope) {
143+
if (scope != null) {
144+
scope.close();
145+
}
146+
}
147+
}
148+
149+
@SuppressWarnings("unused")
150+
public static class HandleResponseAdvice {
151+
152+
@Advice.OnMethodEnter(suppress = Throwable.class)
153+
public static void handleResponseEnter(
154+
@Advice.This HttpClientRequest request,
155+
@Advice.Argument(1) HttpClientResponse response,
156+
@Advice.Local("otelScope") Scope scope) {
157+
Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request);
158+
159+
if (contexts == null) {
160+
return;
161+
}
162+
163+
instrumenter().end(contexts.context, request, response, null);
164+
165+
// Scoping all potential callbacks etc to the parent context
166+
scope = contexts.parentContext.makeCurrent();
167+
}
168+
169+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
170+
public static void handleResponseExit(@Advice.Local("otelScope") Scope scope) {
171+
if (scope != null) {
172+
scope.close();
173+
}
174+
}
175+
}
176+
177+
@SuppressWarnings("unused")
178+
public static class MountContextAdvice {
179+
180+
@Advice.OnMethodEnter(suppress = Throwable.class)
181+
public static void mountContext(
182+
@Advice.This HttpClientRequest request, @Advice.Local("otelScope") Scope scope) {
183+
Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request);
184+
if (contexts == null) {
185+
return;
186+
}
187+
188+
scope = contexts.context.makeCurrent();
189+
}
190+
191+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
192+
public static void unmountContext(@Advice.Local("otelScope") Scope scope) {
193+
if (scope != null) {
194+
scope.close();
195+
}
196+
}
197+
}
198+
199+
@SuppressWarnings("unused")
200+
public static class ExceptionHandlerAdvice {
201+
202+
@Advice.OnMethodEnter(suppress = Throwable.class)
203+
public static void wrapExceptionHandler(
204+
@Advice.This HttpClientRequest request,
205+
@Advice.Argument(value = 0, readOnly = false) Handler<Throwable> handler) {
206+
if (handler != null) {
207+
VirtualField<HttpClientRequest, Contexts> virtualField =
208+
VirtualField.find(HttpClientRequest.class, Contexts.class);
209+
handler = ExceptionHandlerWrapper.wrap(instrumenter(), request, virtualField, handler);
210+
}
211+
}
212+
}
213+
}

0 commit comments

Comments
 (0)