Skip to content

Commit 7f30056

Browse files
123liuziminglaurittrask
authored
support jettyclient 12 (#11519)
Co-authored-by: Lauri Tulmin <[email protected]> Co-authored-by: Trask Stalnaker <[email protected]>
1 parent 26591ea commit 7f30056

File tree

25 files changed

+1049
-19
lines changed

25 files changed

+1049
-19
lines changed

docs/supported-libraries.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ These are the supported libraries and frameworks:
5656
| [Dropwizard Views](https://www.dropwizard.io/en/latest/manual/views.html) | 0.7+ | N/A | Controller Spans [3] |
5757
| [Eclipse Grizzly](https://javaee.github.io/grizzly/httpserverframework.html) | 2.3+ | N/A | [HTTP Server Spans], [HTTP Server Metrics] |
5858
| [Eclipse Jersey](https://eclipse-ee4j.github.io/jersey/) | 2.0+ | N/A | Provides `http.route` [2], Controller Spans [3] |
59-
| [Eclipse Jetty HTTP Client](https://www.eclipse.org/jetty/javadoc/jetty-9/org/eclipse/jetty/client/HttpClient.html) | 9.2+ (not including 10+ yet) | [opentelemetry-jetty-httpclient-9.2](../instrumentation/jetty-httpclient/jetty-httpclient-9.2/library) | [HTTP Client Spans], [HTTP Client Metrics] |
59+
| [Eclipse Jetty HTTP Client](https://www.eclipse.org/jetty/javadoc/jetty-9/org/eclipse/jetty/client/HttpClient.html) | 9.2 - 9.4.x,<br>12.0+ | [opentelemetry-jetty-httpclient-9.2](../instrumentation/jetty-httpclient/jetty-httpclient-9.2/library)<br>[opentelemetry-jetty-httpclient-12.0](../instrumentation/jetty-httpclient/jetty-httpclient-12.0/library) | [HTTP Client Spans], [HTTP Client Metrics] |
6060
| [Eclipse Metro](https://projects.eclipse.org/projects/ee4j.metro) | 2.2+ | N/A | Provides `http.route` [2], Controller Spans [3] |
6161
| [Eclipse Mojarra](https://projects.eclipse.org/projects/ee4j.mojarra) | 1.2+ | N/A | Provides `http.route` [2], Controller Spans [3] |
6262
| [Elasticsearch API Client](https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/index.html) | 7.16 - 7.17.19,<br>8.0 - 8.9.+ [4] | N/A | [Elasticsearch Client Spans] |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
plugins {
2+
id("otel.javaagent-instrumentation")
3+
}
4+
5+
muzzle {
6+
pass {
7+
group.set("org.eclipse.jetty")
8+
module.set("jetty-client")
9+
versions.set("[12,)")
10+
}
11+
}
12+
13+
otelJava {
14+
minJavaVersionSupported.set(JavaVersion.VERSION_17)
15+
}
16+
17+
dependencies {
18+
implementation(project(":instrumentation:jetty-httpclient:jetty-httpclient-12.0:library"))
19+
20+
library("org.eclipse.jetty:jetty-client:12.0.0")
21+
22+
testInstrumentation(project(":instrumentation:jetty-httpclient:jetty-httpclient-9.2:javaagent"))
23+
24+
testImplementation(project(":instrumentation:jetty-httpclient:jetty-httpclient-12.0:testing"))
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v12_0;
7+
8+
import static io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v12_0.JettyHttpClientSingletons.JETTY_CLIENT_CONTEXT_KEY;
9+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
10+
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
11+
import static net.bytebuddy.matcher.ElementMatchers.nameContains;
12+
import static net.bytebuddy.matcher.ElementMatchers.named;
13+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
14+
15+
import io.opentelemetry.context.Context;
16+
import io.opentelemetry.context.Scope;
17+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
18+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
19+
import net.bytebuddy.asm.Advice;
20+
import net.bytebuddy.description.type.TypeDescription;
21+
import net.bytebuddy.matcher.ElementMatcher;
22+
import org.eclipse.jetty.client.Response;
23+
import org.eclipse.jetty.client.Result;
24+
25+
public class JettyClient12ResponseListenersInstrumentation implements TypeInstrumentation {
26+
27+
@Override
28+
public ElementMatcher<TypeDescription> typeMatcher() {
29+
return named("org.eclipse.jetty.client.transport.ResponseListeners");
30+
}
31+
32+
@Override
33+
public void transform(TypeTransformer transformer) {
34+
// for response listeners
35+
transformer.applyAdviceToMethod(
36+
isMethod()
37+
.and(
38+
nameContains("notify")
39+
.and(isPublic())
40+
.and(takesArgument(0, named("org.eclipse.jetty.client.Response")))),
41+
JettyClient12ResponseListenersInstrumentation.class.getName()
42+
+ "$JettyHttpClient12RespListenersNotifyAdvice");
43+
44+
// for complete listeners
45+
transformer.applyAdviceToMethod(
46+
isMethod()
47+
.and(
48+
nameContains("notifyComplete")
49+
.and(isPublic())
50+
.and(takesArgument(0, named("org.eclipse.jetty.client.Result")))),
51+
JettyClient12ResponseListenersInstrumentation.class.getName()
52+
+ "$JettyHttpClient12CompleteListenersNotifyAdvice");
53+
}
54+
55+
@SuppressWarnings("unused")
56+
public static class JettyHttpClient12RespListenersNotifyAdvice {
57+
@Advice.OnMethodEnter(suppress = Throwable.class)
58+
public static void onEnterNotify(
59+
@Advice.Argument(0) Response response,
60+
@Advice.Local("otelContext") Context context,
61+
@Advice.Local("otelScope") Scope scope) {
62+
context = (Context) response.getRequest().getAttributes().get(JETTY_CLIENT_CONTEXT_KEY);
63+
if (context != null) {
64+
scope = context.makeCurrent();
65+
}
66+
}
67+
68+
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
69+
public static void onExitNotify(
70+
@Advice.Argument(0) Response response,
71+
@Advice.Thrown Throwable throwable,
72+
@Advice.Local("otelContext") Context context,
73+
@Advice.Local("otelScope") Scope scope) {
74+
if (scope == null) {
75+
return;
76+
}
77+
78+
scope.close();
79+
}
80+
}
81+
82+
@SuppressWarnings("unused")
83+
public static class JettyHttpClient12CompleteListenersNotifyAdvice {
84+
@Advice.OnMethodEnter(suppress = Throwable.class)
85+
public static void onEnterComplete(
86+
@Advice.Argument(0) Result result,
87+
@Advice.Local("otelContext") Context context,
88+
@Advice.Local("otelScope") Scope scope) {
89+
context = (Context) result.getRequest().getAttributes().get(JETTY_CLIENT_CONTEXT_KEY);
90+
if (context != null) {
91+
scope = context.makeCurrent();
92+
}
93+
}
94+
95+
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
96+
public static void onExitComplete(
97+
@Advice.Argument(0) Result result,
98+
@Advice.Thrown Throwable throwable,
99+
@Advice.Local("otelContext") Context context,
100+
@Advice.Local("otelScope") Scope scope) {
101+
if (scope == null) {
102+
return;
103+
}
104+
105+
scope.close();
106+
}
107+
}
108+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v12_0;
7+
8+
import static io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v12_0.JettyHttpClientSingletons.JETTY_CLIENT_CONTEXT_KEY;
9+
import static io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v12_0.JettyHttpClientSingletons.instrumenter;
10+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
11+
import static net.bytebuddy.matcher.ElementMatchers.nameContains;
12+
import static net.bytebuddy.matcher.ElementMatchers.named;
13+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
14+
15+
import io.opentelemetry.context.Context;
16+
import io.opentelemetry.context.Scope;
17+
import io.opentelemetry.instrumentation.jetty.httpclient.v12_0.internal.JettyClientTracingListener;
18+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
19+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
20+
import net.bytebuddy.asm.Advice;
21+
import net.bytebuddy.description.type.TypeDescription;
22+
import net.bytebuddy.matcher.ElementMatcher;
23+
import org.eclipse.jetty.client.transport.HttpRequest;
24+
25+
public class JettyHttpClient12Instrumentation implements TypeInstrumentation {
26+
27+
@Override
28+
public ElementMatcher<TypeDescription> typeMatcher() {
29+
return named("org.eclipse.jetty.client.transport.HttpRequest");
30+
}
31+
32+
@Override
33+
public void transform(TypeTransformer transformer) {
34+
transformer.applyAdviceToMethod(
35+
isMethod()
36+
.and(named("send"))
37+
.and(takesArgument(0, named("org.eclipse.jetty.client.Response$CompleteListener"))),
38+
JettyHttpClient12Instrumentation.class.getName() + "$JettyHttpClient12SendAdvice");
39+
// For request listeners
40+
transformer.applyAdviceToMethod(
41+
isMethod().and(nameContains("notify")),
42+
JettyHttpClient12Instrumentation.class.getName() + "$JettyHttpClient12NotifyAdvice");
43+
}
44+
45+
@SuppressWarnings("unused")
46+
public static class JettyHttpClient12SendAdvice {
47+
48+
@Advice.OnMethodEnter(suppress = Throwable.class)
49+
public static void onEnterSend(
50+
@Advice.This HttpRequest request,
51+
@Advice.Local("otelContext") Context context,
52+
@Advice.Local("otelScope") Scope scope) {
53+
// start span
54+
Context parentContext = Context.current();
55+
context = JettyClientTracingListener.handleRequest(parentContext, request, instrumenter());
56+
if (context == null) {
57+
return;
58+
}
59+
// set context for responseListeners
60+
request.attribute(JETTY_CLIENT_CONTEXT_KEY, parentContext);
61+
62+
scope = context.makeCurrent();
63+
}
64+
65+
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
66+
public static void onExitSend(
67+
@Advice.This HttpRequest request,
68+
@Advice.Thrown Throwable throwable,
69+
@Advice.Local("otelContext") Context context,
70+
@Advice.Local("otelScope") Scope scope) {
71+
if (scope == null) {
72+
return;
73+
}
74+
75+
// not ending span here unless error, span ended in the interceptor
76+
scope.close();
77+
if (throwable != null) {
78+
instrumenter().end(context, request, null, throwable);
79+
}
80+
}
81+
}
82+
83+
@SuppressWarnings("unused")
84+
public static class JettyHttpClient12NotifyAdvice {
85+
@Advice.OnMethodEnter(suppress = Throwable.class)
86+
public static void onEnterNotify(
87+
@Advice.This HttpRequest request,
88+
@Advice.Local("otelContext") Context context,
89+
@Advice.Local("otelScope") Scope scope) {
90+
context = (Context) request.getAttributes().get(JETTY_CLIENT_CONTEXT_KEY);
91+
if (context == null) {
92+
return;
93+
}
94+
scope = context.makeCurrent();
95+
}
96+
97+
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
98+
public static void onExitNotify(
99+
@Advice.Local("otelContext") Context context, @Advice.Local("otelScope") Scope scope) {
100+
if (scope == null) {
101+
return;
102+
}
103+
104+
scope.close();
105+
}
106+
}
107+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v12_0;
7+
8+
import static java.util.Arrays.asList;
9+
10+
import com.google.auto.service.AutoService;
11+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
12+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
13+
import java.util.List;
14+
15+
@AutoService(InstrumentationModule.class)
16+
public class JettyHttpClient12InstrumentationModule extends InstrumentationModule {
17+
public JettyHttpClient12InstrumentationModule() {
18+
super("jetty-httpclient", "jetty-httpclient-12.0");
19+
}
20+
21+
@Override
22+
public List<TypeInstrumentation> typeInstrumentations() {
23+
return asList(
24+
new JettyHttpClient12Instrumentation(),
25+
new JettyClient12ResponseListenersInstrumentation());
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v12_0;
7+
8+
import io.opentelemetry.api.GlobalOpenTelemetry;
9+
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
10+
import io.opentelemetry.instrumentation.jetty.httpclient.v12_0.internal.JettyHttpClientInstrumenterBuilderFactory;
11+
import io.opentelemetry.javaagent.bootstrap.internal.JavaagentHttpClientInstrumenters;
12+
import org.eclipse.jetty.client.Request;
13+
import org.eclipse.jetty.client.Response;
14+
15+
public final class JettyHttpClientSingletons {
16+
17+
static final String JETTY_CLIENT_CONTEXT_KEY = "otel-jetty-client-context";
18+
19+
private static final Instrumenter<Request, Response> INSTRUMENTER =
20+
JavaagentHttpClientInstrumenters.create(
21+
JettyHttpClientInstrumenterBuilderFactory.create(GlobalOpenTelemetry.get()));
22+
23+
public static Instrumenter<Request, Response> instrumenter() {
24+
return INSTRUMENTER;
25+
}
26+
27+
private JettyHttpClientSingletons() {}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v12_0;
7+
8+
import io.opentelemetry.instrumentation.jetty.httpclient.v12_0.AbstractJettyClient12Test;
9+
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
10+
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension;
11+
import org.eclipse.jetty.client.HttpClient;
12+
import org.eclipse.jetty.util.ssl.SslContextFactory;
13+
import org.junit.jupiter.api.extension.RegisterExtension;
14+
15+
class JettyHttpClient12AgentTest extends AbstractJettyClient12Test {
16+
17+
@RegisterExtension
18+
static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent();
19+
20+
@Override
21+
protected HttpClient createStandardClient() {
22+
return new HttpClient();
23+
}
24+
25+
@Override
26+
protected HttpClient createHttpsClient(SslContextFactory.Client sslContextFactory) {
27+
HttpClient httpClient = new HttpClient();
28+
httpClient.setSslContextFactory(sslContextFactory);
29+
return httpClient;
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
plugins {
2+
id("otel.library-instrumentation")
3+
}
4+
5+
otelJava {
6+
minJavaVersionSupported.set(JavaVersion.VERSION_17)
7+
}
8+
9+
dependencies {
10+
library("org.eclipse.jetty:jetty-client:12.0.0")
11+
12+
testImplementation(project(":instrumentation:jetty-httpclient::jetty-httpclient-12.0:testing"))
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.jetty.httpclient.v12_0;
7+
8+
import io.opentelemetry.api.OpenTelemetry;
9+
import org.eclipse.jetty.client.HttpClient;
10+
11+
/** Entrypoint for instrumenting Jetty client. */
12+
public final class JettyClientTelemetry {
13+
14+
/** Returns a new {@link JettyClientTelemetry} configured with the given {@link OpenTelemetry}. */
15+
public static JettyClientTelemetry create(OpenTelemetry openTelemetry) {
16+
JettyClientTelemetryBuilder builder = builder(openTelemetry);
17+
return builder.build();
18+
}
19+
20+
/**
21+
* Returns a new {@link JettyClientTelemetryBuilder} configured with the given {@link
22+
* OpenTelemetry}.
23+
*/
24+
public static JettyClientTelemetryBuilder builder(OpenTelemetry openTelemetry) {
25+
return new JettyClientTelemetryBuilder(openTelemetry);
26+
}
27+
28+
private final HttpClient httpClient;
29+
30+
JettyClientTelemetry(HttpClient httpClient) {
31+
this.httpClient = httpClient;
32+
}
33+
34+
public HttpClient getHttpClient() {
35+
return httpClient;
36+
}
37+
}

0 commit comments

Comments
 (0)