Skip to content

Commit

Permalink
Support jetty client 12 (#7305)
Browse files Browse the repository at this point in the history
* Support jetty client 12

* Use noop scope when no parent

* clean up
  • Loading branch information
amarziali committed Jul 12, 2024
1 parent 95a52cc commit 63f16d7
Show file tree
Hide file tree
Showing 13 changed files with 577 additions and 0 deletions.
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

0 comments on commit 63f16d7

Please sign in to comment.