Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support jetty client 12 #7305

Merged
merged 4 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
ext {
minJavaVersionForTests = JavaVersion.VERSION_17
}
muzzle {
pass {
group = "org.eclipse.jetty"
module = "jetty-client"
versions = "[12,)"
javaVersion = "17"
assertInverse = true
}
}

apply from: "$rootDir/gradle/java.gradle"

addTestSuiteForDir('latestDepTest', 'test')

compileMain_java17Java.configure {
setJavaVersion(it, 17)
}

configurations.matching({ it.name.startsWith('test') || it.name.startsWith('latestDepTest') }).each({
it.resolutionStrategy {
force group: 'org.slf4j', name: 'slf4j-api', version: libs.versions.slf4j.get()
}
})

dependencies {
main_java17CompileOnly group: 'org.eclipse.jetty', name: 'jetty-client', version: '12.0.0'
// to test conflicts
testImplementation(project(':dd-java-agent:instrumentation:jetty-client:jetty-client-9.1'))
testImplementation(project(':dd-java-agent:instrumentation:jetty-client:jetty-client-10.0'))

testImplementation(project(path:':dd-java-agent:testing', configuration:'shadow')) {
// explicitly declared below.
exclude group: 'org.eclipse.jetty'
}
testImplementation project(':dd-java-agent:instrumentation:jetty-util')
testImplementation group: 'org.eclipse.jetty', name: 'jetty-client', version: '12.0.0'
latestDepTestImplementation group: 'org.eclipse.jetty', name: 'jetty-client', version: '12.+'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package datadog.trace.instrumentation.jetty_client12;

import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;

import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.agent.tooling.InstrumenterModule;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import java.util.HashMap;
import java.util.Map;

@AutoService(InstrumenterModule.class)
public final class FutureResponseListenerInstrumentation extends InstrumenterModule.Tracing
implements Instrumenter.ForSingleType {

public FutureResponseListenerInstrumentation() {
super("jetty-client");
}

@Override
public String instrumentedType() {
return "org.eclipse.jetty.client.FutureResponseListener";
}

@Override
public Map<String, String> contextStore() {
Map<String, String> contextStore = new HashMap<>(4);
contextStore.put("org.eclipse.jetty.client.Request", AgentSpan.class.getName());
contextStore.put(
"org.eclipse.jetty.client.FutureResponseListener", "org.eclipse.jetty.client.Request");
return contextStore;
}

@Override
public void methodAdvice(MethodTransformer transformer) {
transformer.applyAdvice(
isConstructor()
.and(
takesArgument(0, named("org.eclipse.jetty.client.Request")).and(takesArguments(2))),
packageName + ".LinkListenerAdvice");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package datadog.trace.instrumentation.jetty_client12;

import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf;
import static datadog.trace.bootstrap.instrumentation.java.concurrent.ExcludeFilter.ExcludeType.RUNNABLE;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;

import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.ExcludeFilterProvider;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.agent.tooling.InstrumenterModule;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.java.concurrent.ExcludeFilter;
import java.util.Collection;
import java.util.Map;

@AutoService(InstrumenterModule.class)
public class JettyHttpClientInstrumentation extends InstrumenterModule.Tracing
implements Instrumenter.ForSingleType, ExcludeFilterProvider {
public JettyHttpClientInstrumentation() {
super("jetty-client");
}

@Override
public String instrumentedType() {
return "org.eclipse.jetty.client.transport.HttpRequest";
}

@Override
public String[] helperClassNames() {
return new String[] {
packageName + ".JettyClientDecorator",
packageName + ".HeadersInjectAdapter",
packageName + ".SpanFinishingCompleteListener",
packageName + ".CallbackWrapper",
};
}

@Override
public Map<String, String> contextStore() {
return singletonMap("org.eclipse.jetty.client.Request", AgentSpan.class.getName());
}

@Override
public void methodAdvice(MethodTransformer transformer) {
transformer.applyAdvice(isConstructor(), packageName + ".RequestCreateAdvice");
transformer.applyAdvice(
isMethod()
.and(named("send"))
.and(takesArgument(0, named("org.eclipse.jetty.client.Response$CompleteListener"))),
packageName + ".SendAdvice");
transformer.applyAdvice(
isMethod()
.and(isPublic())
.and(namedOneOf("listener", "onSuccess", "onFailure", "onComplete"))
.and(takesArguments(1)),
packageName + ".WrapListenerAdvice");
}

@Override
public Map<ExcludeFilter.ExcludeType, ? extends Collection<String>> excludedClasses() {
return singletonMap(RUNNABLE, singletonList("org.eclipse.jetty.util.SocketAddressResolver$1"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package datadog.trace.instrumentation.jetty_client12;

import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.noopSpan;

import datadog.trace.bootstrap.instrumentation.api.AgentScope;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import org.eclipse.jetty.client.Request;
import org.eclipse.jetty.client.Response;
import org.eclipse.jetty.client.Result;

public class CallbackWrapper implements Response.Listener, Request.Listener {

private final AgentSpan parent;
private final AgentSpan span;
private final Object delegate;

public CallbackWrapper(AgentSpan parent, AgentSpan span, Object delegate) {
this.parent = parent != null ? parent : noopSpan();
this.span = span;
this.delegate = delegate;
}

@Override
public void onBegin(Response response) {
if (delegate instanceof Response.BeginListener) {
try (AgentScope scope = activate(span)) {
((Response.BeginListener) delegate).onBegin(response);
}
}
}

@Override
public void onComplete(Result result) {
if (delegate instanceof Response.CompleteListener) {
try (AgentScope scope = activate(parent)) {
((Response.CompleteListener) delegate).onComplete(result);
}
}
}

@Override
public void onFailure(Response response, Throwable failure) {
if (delegate instanceof Response.FailureListener) {
try (AgentScope scope = activate(span)) {
((Response.FailureListener) delegate).onFailure(response, failure);
}
}
}

@Override
public void onHeaders(Response response) {
if (delegate instanceof Response.HeadersListener) {
try (AgentScope scope = activate(span)) {
((Response.HeadersListener) delegate).onHeaders(response);
}
}
}

@Override
public void onSuccess(Response response) {
if (delegate instanceof Response.SuccessListener) {
try (AgentScope scope = activate(span)) {
((Response.SuccessListener) delegate).onSuccess(response);
}
}
}

@Override
public void onBegin(Request request) {
if (delegate instanceof Request.BeginListener) {
try (AgentScope scope = activate(span)) {
((Request.SuccessListener) delegate).onSuccess(request);
}
}
}

@Override
public void onCommit(Request request) {
if (delegate instanceof Request.CommitListener) {
try (AgentScope scope = activate(span)) {
((Request.CommitListener) delegate).onCommit(request);
}
}
}

@Override
public void onFailure(Request request, Throwable failure) {
if (delegate instanceof Request.FailureListener) {
try (AgentScope scope = activate(span)) {
((Request.FailureListener) delegate).onFailure(request, failure);
}
}
}

@Override
public void onHeaders(Request request) {
if (delegate instanceof Request.HeadersListener) {
try (AgentScope scope = activate(span)) {
((Request.HeadersListener) delegate).onHeaders(request);
}
}
}

@Override
public void onQueued(Request request) {
if (delegate instanceof Request.QueuedListener) {
try (AgentScope scope = activate(span)) {
((Request.QueuedListener) delegate).onQueued(request);
}
}
}

@Override
public void onSuccess(Request request) {
if (delegate instanceof Request.SuccessListener) {
try (AgentScope scope = activate(span)) {
((Request.SuccessListener) delegate).onSuccess(request);
}
}
}

private AgentScope activate(AgentSpan span) {
return null == span ? null : activateSpan(span);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package datadog.trace.instrumentation.jetty_client12;

import datadog.trace.bootstrap.instrumentation.api.AgentPropagation;
import org.eclipse.jetty.client.Request;

public class HeadersInjectAdapter implements AgentPropagation.Setter<Request> {

public static final HeadersInjectAdapter SETTER = new HeadersInjectAdapter();

@Override
public void set(final Request carrier, final String key, final String value) {
carrier.headers(httpFields -> httpFields.add(key, value));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package datadog.trace.instrumentation.jetty_client12;

import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString;
import datadog.trace.bootstrap.instrumentation.decorator.HttpClientDecorator;
import java.net.URI;
import org.eclipse.jetty.client.Request;
import org.eclipse.jetty.client.Response;

public class JettyClientDecorator extends HttpClientDecorator<Request, Response> {
public static final CharSequence JETTY_CLIENT = UTF8BytesString.create("jetty-client");
public static final JettyClientDecorator DECORATE = new JettyClientDecorator();
public static final CharSequence HTTP_REQUEST = UTF8BytesString.create(DECORATE.operationName());

@Override
protected String[] instrumentationNames() {
return new String[] {"jetty-client"};
}

@Override
protected CharSequence component() {
return JETTY_CLIENT;
}

@Override
protected String method(final Request httpRequest) {
return httpRequest.getMethod();
}

@Override
protected URI url(final Request httpRequest) {
return httpRequest.getURI();
}

@Override
protected int status(final Response httpResponse) {
return httpResponse.getStatus();
}

@Override
protected String getRequestHeader(Request request, String headerName) {
return request.getHeaders().get(headerName);
}

@Override
protected String getResponseHeader(Response response, String headerName) {
return response.getHeaders().get(headerName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package datadog.trace.instrumentation.jetty_client12;

import datadog.trace.bootstrap.InstrumentationContext;
import net.bytebuddy.asm.Advice;
import org.eclipse.jetty.client.FutureResponseListener;
import org.eclipse.jetty.client.Request;

public class LinkListenerAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void link(
@Advice.This FutureResponseListener listener, @Advice.Argument(0) Request request) {
InstrumentationContext.get(FutureResponseListener.class, Request.class).put(listener, request);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package datadog.trace.instrumentation.jetty_client12;

import datadog.trace.bootstrap.InstrumentationContext;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import net.bytebuddy.asm.Advice;
import org.eclipse.jetty.client.Request;
import org.eclipse.jetty.client.transport.HttpRequest;

public class RequestCreateAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void afterCreate(@Advice.This HttpRequest self) {
self.onComplete(
new SpanFinishingCompleteListener(
InstrumentationContext.get(Request.class, AgentSpan.class)));
}
}
Loading
Loading