-
Notifications
You must be signed in to change notification settings - Fork 336
Ratpack instrumentation support #276
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
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
6e92221
Ratpack instrumentaiton support
jonmort 6c1c830
Apply review comments
jonmort 582c0af
Rename to Ratpack 1.4
jonmort 1ba0b69
Resource naming and other review comments
jonmort 18da244
Correctly look for classes to determine minimum ratpack version
jonmort 4875165
Only build ratpack when using a java 8 compiler
jonmort fa90af7
Introduce a java 8 source set for Ratpack integration
jonmort b4510b7
Add a guard against wrapping the ratpack request action multiple times
jonmort 35ad8c9
Tidy up RatpackTest comments
jonmort File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
50 changes: 50 additions & 0 deletions
50
dd-java-agent/instrumentation/ratpack-1.4/ratpack-1.4.gradle
This file contains hidden or 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,50 @@ | ||
| apply plugin: 'version-scan' | ||
|
|
||
| versionScan { | ||
| group = "io.ratpack" | ||
| module = 'ratpack' | ||
| versions = "[1.4.0,)" | ||
| verifyPresent = [ | ||
| "ratpack.path.PathBinding": "getDescription", | ||
| ] | ||
| } | ||
|
|
||
| apply from: "${rootDir}/gradle/java.gradle" | ||
|
|
||
| /* | ||
| Here we introduce a sourceSet for the java 8 code which needs to be compiled with a source and target of 1.8 | ||
| The instrumentation classes must be compiled with java 7 and do nothing when ratpack is not on the classpath. The | ||
| java 8 classes are used lazily so there is no direct linking between the 1.7 and 1.8 bytecode. | ||
| */ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the nice comment. |
||
| sourceSets { | ||
| main_java8 { | ||
| java.srcDirs "${project.projectDir}/src/main/java8" | ||
| } | ||
| } | ||
|
|
||
| compileMain_java8Java { | ||
| sourceCompatibility = 1.8 | ||
| targetCompatibility = 1.8 | ||
| } | ||
|
|
||
| dependencies { | ||
| main_java8CompileOnly group: 'io.ratpack', name: 'ratpack-core', version: '1.4.0' | ||
|
|
||
| main_java8Compile project(':dd-trace-ot') | ||
| main_java8Compile project(':dd-java-agent:agent-tooling') | ||
|
|
||
| main_java8Compile deps.bytebuddy | ||
| main_java8Compile deps.opentracing | ||
| main_java8Compile deps.autoservice | ||
|
|
||
| compileOnly sourceSets.main_java8.compileClasspath | ||
|
|
||
| compile sourceSets.main_java8.output | ||
|
|
||
| testCompile project(':dd-java-agent:testing') | ||
| testCompile group: 'io.ratpack', name: 'ratpack-test', version: '1.4.0' | ||
|
|
||
| testCompile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.6.0' | ||
| } | ||
|
|
||
| testJava8Only += '**/RatpackTest.class' | ||
75 changes: 75 additions & 0 deletions
75
...src/main/java/datadog/trace/instrumentation/ratpack/RatpackHttpClientInstrumentation.java
This file contains hidden or 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,75 @@ | ||
| package datadog.trace.instrumentation.ratpack; | ||
|
|
||
| import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; | ||
| import static net.bytebuddy.matcher.ElementMatchers.isInterface; | ||
| import static net.bytebuddy.matcher.ElementMatchers.named; | ||
| import static net.bytebuddy.matcher.ElementMatchers.not; | ||
| import static net.bytebuddy.matcher.ElementMatchers.takesArguments; | ||
|
|
||
| import com.google.auto.service.AutoService; | ||
| import datadog.trace.agent.tooling.DDAdvice; | ||
| import datadog.trace.agent.tooling.HelperInjector; | ||
| import datadog.trace.agent.tooling.Instrumenter; | ||
| import datadog.trace.instrumentation.ratpack.impl.RatpackHttpClientAdvice; | ||
| import java.net.URI; | ||
| import net.bytebuddy.agent.builder.AgentBuilder; | ||
| import net.bytebuddy.description.type.TypeDescription; | ||
|
|
||
| @AutoService(Instrumenter.class) | ||
| public final class RatpackHttpClientInstrumentation extends Instrumenter.Configurable { | ||
|
|
||
| private static final HelperInjector HTTP_CLIENT_HELPER_INJECTOR = | ||
| new HelperInjector( | ||
| "datadog.trace.instrumentation.ratpack.impl.RatpackHttpClientAdvice$RatpackHttpClientRequestAdvice", | ||
| "datadog.trace.instrumentation.ratpack.impl.RatpackHttpClientAdvice$RatpackHttpClientRequestStreamAdvice", | ||
| "datadog.trace.instrumentation.ratpack.impl.RatpackHttpClientAdvice$RatpackHttpGetAdvice"); | ||
| public static final TypeDescription.ForLoadedType URI_TYPE_DESCRIPTION = | ||
| new TypeDescription.ForLoadedType(URI.class); | ||
|
|
||
| public RatpackHttpClientInstrumentation() { | ||
| super(RatpackInstrumentation.EXEC_NAME); | ||
| } | ||
|
|
||
| @Override | ||
| protected boolean defaultEnabled() { | ||
| return false; | ||
| } | ||
|
|
||
| @Override | ||
| public AgentBuilder apply(final AgentBuilder agentBuilder) { | ||
|
|
||
| return agentBuilder | ||
| .type( | ||
| not(isInterface()).and(hasSuperType(named("ratpack.http.client.HttpClient"))), | ||
| RatpackInstrumentation.CLASSLOADER_CONTAINS_RATPACK_1_4_OR_ABOVE) | ||
| .transform(HTTP_CLIENT_HELPER_INJECTOR) | ||
| .transform( | ||
| DDAdvice.create() | ||
| .advice( | ||
| named("request") | ||
| .and( | ||
| takesArguments( | ||
| URI_TYPE_DESCRIPTION, | ||
| RatpackInstrumentation.ACTION_TYPE_DESCRIPTION)), | ||
| RatpackHttpClientAdvice.RatpackHttpClientRequestAdvice.class.getName())) | ||
| .transform( | ||
| DDAdvice.create() | ||
| .advice( | ||
| named("requestStream") | ||
| .and( | ||
| takesArguments( | ||
| URI_TYPE_DESCRIPTION, | ||
| RatpackInstrumentation.ACTION_TYPE_DESCRIPTION)), | ||
| RatpackHttpClientAdvice.RatpackHttpClientRequestStreamAdvice.class.getName())) | ||
| .transform( | ||
| DDAdvice.create() | ||
| .advice( | ||
| named("get") | ||
| .and( | ||
| takesArguments( | ||
| URI_TYPE_DESCRIPTION, | ||
| RatpackInstrumentation.ACTION_TYPE_DESCRIPTION)), | ||
| RatpackHttpClientAdvice.RatpackHttpGetAdvice.class.getName())) | ||
| .asDecorator(); | ||
| } | ||
| } |
84 changes: 84 additions & 0 deletions
84
...tpack-1.4/src/main/java/datadog/trace/instrumentation/ratpack/RatpackInstrumentation.java
This file contains hidden or 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,84 @@ | ||
| package datadog.trace.instrumentation.ratpack; | ||
|
|
||
| import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClassWithMethod; | ||
| import static net.bytebuddy.matcher.ElementMatchers.*; | ||
|
|
||
| import com.google.auto.service.AutoService; | ||
| import datadog.trace.agent.tooling.DDAdvice; | ||
| import datadog.trace.agent.tooling.HelperInjector; | ||
| import datadog.trace.agent.tooling.Instrumenter; | ||
| import datadog.trace.instrumentation.ratpack.impl.RatpackServerAdvice; | ||
| import java.lang.reflect.Modifier; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import net.bytebuddy.agent.builder.AgentBuilder; | ||
| import net.bytebuddy.description.type.TypeDescription; | ||
| import net.bytebuddy.matcher.ElementMatcher; | ||
|
|
||
| @AutoService(Instrumenter.class) | ||
| @Slf4j | ||
| public final class RatpackInstrumentation extends Instrumenter.Configurable { | ||
|
|
||
| static final String EXEC_NAME = "ratpack"; | ||
| private static final HelperInjector SERVER_REGISTRY_HELPER_INJECTOR = | ||
| new HelperInjector( | ||
| "datadog.trace.instrumentation.ratpack.impl.RatpackScopeManager", | ||
| "datadog.trace.instrumentation.ratpack.impl.TracingHandler", | ||
| "datadog.trace.instrumentation.ratpack.impl.RatpackServerAdvice$RatpackServerRegistryAdvice"); | ||
| private static final HelperInjector EXEC_STARTER_HELPER_INJECTOR = | ||
| new HelperInjector( | ||
| "datadog.trace.instrumentation.ratpack.impl.RatpackServerAdvice$ExecStarterAdvice", | ||
| "datadog.trace.instrumentation.ratpack.impl.RatpackServerAdvice$ExecStarterAction"); | ||
|
|
||
| static final TypeDescription.Latent ACTION_TYPE_DESCRIPTION = | ||
| new TypeDescription.Latent("ratpack.func.Action", Modifier.PUBLIC, null); | ||
|
|
||
| static final ElementMatcher.Junction.AbstractBase<ClassLoader> | ||
| CLASSLOADER_CONTAINS_RATPACK_1_4_OR_ABOVE = | ||
| classLoaderHasClassWithMethod("ratpack.path.PathBinding", "getDescription"); | ||
|
|
||
| public RatpackInstrumentation() { | ||
| super(EXEC_NAME); | ||
| } | ||
|
|
||
| @Override | ||
| protected boolean defaultEnabled() { | ||
| return false; | ||
| } | ||
|
|
||
| @Override | ||
| public AgentBuilder apply(final AgentBuilder agentBuilder) { | ||
|
|
||
| return agentBuilder | ||
| .type( | ||
| named("ratpack.server.internal.ServerRegistry"), | ||
| CLASSLOADER_CONTAINS_RATPACK_1_4_OR_ABOVE) | ||
| .transform(SERVER_REGISTRY_HELPER_INJECTOR) | ||
| .transform( | ||
| DDAdvice.create() | ||
| .advice( | ||
| isMethod().and(isStatic()).and(named("buildBaseRegistry")), | ||
| RatpackServerAdvice.RatpackServerRegistryAdvice.class.getName())) | ||
| .asDecorator() | ||
| .type( | ||
| not(isInterface()).and(hasSuperType(named("ratpack.exec.ExecStarter"))), | ||
| CLASSLOADER_CONTAINS_RATPACK_1_4_OR_ABOVE) | ||
| .transform(EXEC_STARTER_HELPER_INJECTOR) | ||
| .transform( | ||
| DDAdvice.create() | ||
| .advice( | ||
| named("register").and(takesArguments(ACTION_TYPE_DESCRIPTION)), | ||
| RatpackServerAdvice.ExecStarterAdvice.class.getName())) | ||
| .asDecorator() | ||
| .type( | ||
| named("ratpack.exec.Execution") | ||
| .or(not(isInterface()).and(hasSuperType(named("ratpack.exec.Execution")))), | ||
| CLASSLOADER_CONTAINS_RATPACK_1_4_OR_ABOVE) | ||
| .transform(EXEC_STARTER_HELPER_INJECTOR) | ||
| .transform( | ||
| DDAdvice.create() | ||
| .advice( | ||
| named("fork").and(returns(named("ratpack.exec.ExecStarter"))), | ||
| RatpackServerAdvice.ExecutionAdvice.class.getName())) | ||
| .asDecorator(); | ||
| } | ||
| } |
144 changes: 144 additions & 0 deletions
144
....4/src/main/java8/datadog/trace/instrumentation/ratpack/impl/RatpackHttpClientAdvice.java
This file contains hidden or 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,144 @@ | ||
| package datadog.trace.instrumentation.ratpack.impl; | ||
|
|
||
| import static io.opentracing.log.Fields.ERROR_OBJECT; | ||
|
|
||
| import io.opentracing.Span; | ||
| import io.opentracing.tag.Tags; | ||
| import io.opentracing.util.GlobalTracer; | ||
| import java.util.Collections; | ||
| import java.util.concurrent.atomic.AtomicReference; | ||
| import net.bytebuddy.asm.Advice; | ||
| import ratpack.exec.Promise; | ||
| import ratpack.exec.Result; | ||
| import ratpack.func.Action; | ||
| import ratpack.http.client.ReceivedResponse; | ||
| import ratpack.http.client.RequestSpec; | ||
| import ratpack.http.client.StreamedResponse; | ||
|
|
||
| public class RatpackHttpClientAdvice { | ||
| public static class RequestAction implements Action<RequestSpec> { | ||
|
|
||
| private final Action<? super RequestSpec> requestAction; | ||
| private final AtomicReference<Span> spanRef; | ||
|
|
||
| public RequestAction(Action<? super RequestSpec> requestAction, AtomicReference<Span> spanRef) { | ||
| this.requestAction = requestAction; | ||
| this.spanRef = spanRef; | ||
| } | ||
|
|
||
| @Override | ||
| public void execute(RequestSpec requestSpec) throws Exception { | ||
| WrappedRequestSpec wrappedRequestSpec; | ||
| if (requestSpec instanceof WrappedRequestSpec) { | ||
| wrappedRequestSpec = (WrappedRequestSpec) requestSpec; | ||
| } else { | ||
| wrappedRequestSpec = | ||
| new WrappedRequestSpec( | ||
| requestSpec, | ||
| GlobalTracer.get(), | ||
| GlobalTracer.get().scopeManager().active(), | ||
| spanRef); | ||
| } | ||
| requestAction.execute(wrappedRequestSpec); | ||
| } | ||
| } | ||
|
|
||
| public static class ResponseAction implements Action<Result<ReceivedResponse>> { | ||
| private final AtomicReference<Span> spanRef; | ||
|
|
||
| public ResponseAction(AtomicReference<Span> spanRef) { | ||
| this.spanRef = spanRef; | ||
| } | ||
|
|
||
| @Override | ||
| public void execute(Result<ReceivedResponse> result) { | ||
| Span span = spanRef.get(); | ||
| if (span == null) { | ||
| return; | ||
| } | ||
| span.finish(); | ||
| if (result.isError()) { | ||
| Tags.ERROR.set(span, true); | ||
| span.log(Collections.singletonMap(ERROR_OBJECT, result.getThrowable())); | ||
| } else { | ||
| Tags.HTTP_STATUS.set(span, result.getValue().getStatusCode()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public static class StreamedResponseAction implements Action<Result<StreamedResponse>> { | ||
| private final Span span; | ||
|
|
||
| public StreamedResponseAction(Span span) { | ||
| this.span = span; | ||
| } | ||
|
|
||
| @Override | ||
| public void execute(Result<StreamedResponse> result) { | ||
| span.finish(); | ||
| if (result.isError()) { | ||
| Tags.ERROR.set(span, true); | ||
| span.log(Collections.singletonMap(ERROR_OBJECT, result.getThrowable())); | ||
| } else { | ||
| Tags.HTTP_STATUS.set(span, result.getValue().getStatusCode()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public static class RatpackHttpClientRequestAdvice { | ||
| @Advice.OnMethodEnter | ||
| public static AtomicReference<Span> injectTracing( | ||
| @Advice.Argument(value = 1, readOnly = false) Action<? super RequestSpec> requestAction) { | ||
| AtomicReference<Span> span = new AtomicReference<>(); | ||
|
|
||
| //noinspection UnusedAssignment | ||
| requestAction = new RequestAction(requestAction, span); | ||
|
|
||
| return span; | ||
| } | ||
|
|
||
| @Advice.OnMethodExit | ||
| public static void finishTracing( | ||
| @Advice.Return(readOnly = false) Promise<ReceivedResponse> promise, | ||
| @Advice.Enter AtomicReference<Span> ref) { | ||
|
|
||
| //noinspection UnusedAssignment | ||
| promise = promise.wiretap(new ResponseAction(ref)); | ||
| } | ||
| } | ||
|
|
||
| public static class RatpackHttpClientRequestStreamAdvice { | ||
| @Advice.OnMethodEnter | ||
| public static AtomicReference<Span> injectTracing( | ||
| @Advice.Argument(value = 1, readOnly = false) Action<? super RequestSpec> requestAction) { | ||
| AtomicReference<Span> span = new AtomicReference<>(); | ||
|
|
||
| //noinspection UnusedAssignment | ||
| requestAction = new RequestAction(requestAction, span); | ||
|
|
||
| return span; | ||
| } | ||
|
|
||
| @Advice.OnMethodExit | ||
| public static void finishTracing( | ||
| @Advice.Return(readOnly = false) Promise<StreamedResponse> promise, | ||
| @Advice.Enter AtomicReference<Span> ref) { | ||
| Span span = ref.get(); | ||
| if (span == null) { | ||
| return; | ||
| } | ||
|
|
||
| //noinspection UnusedAssignment | ||
| promise = promise.wiretap(new StreamedResponseAction(span)); | ||
| } | ||
| } | ||
|
|
||
| public static class RatpackHttpGetAdvice { | ||
| @Advice.OnMethodEnter | ||
| public static void ensureGetMethodSet( | ||
| @Advice.Argument(value = 1, readOnly = false) Action<? super RequestSpec> requestAction) { | ||
| //noinspection UnusedAssignment | ||
| requestAction = requestAction.prepend(RequestSpec::get); | ||
| } | ||
| } | ||
| } |
29 changes: 29 additions & 0 deletions
29
...c/main/java8/datadog/trace/instrumentation/ratpack/impl/RatpackRequestExtractAdapter.java
This file contains hidden or 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,29 @@ | ||
| package datadog.trace.instrumentation.ratpack.impl; | ||
|
|
||
| import com.google.common.collect.ListMultimap; | ||
| import io.opentracing.propagation.TextMap; | ||
| import java.util.Iterator; | ||
| import java.util.Map; | ||
| import ratpack.http.Request; | ||
|
|
||
| /** | ||
| * Simple request extractor in the same vein as @see | ||
| * io.opentracing.contrib.web.servlet.filter.HttpServletRequestExtractAdapter | ||
| */ | ||
| public class RatpackRequestExtractAdapter implements TextMap { | ||
| private final ListMultimap<String, String> headers; | ||
|
|
||
| RatpackRequestExtractAdapter(Request request) { | ||
| this.headers = request.getHeaders().asMultiValueMap().asMultimap(); | ||
| } | ||
|
|
||
| @Override | ||
| public Iterator<Map.Entry<String, String>> iterator() { | ||
| return headers.entries().iterator(); | ||
| } | ||
|
|
||
| @Override | ||
| public void put(String key, String value) { | ||
| throw new UnsupportedOperationException("This class should be used only with Tracer.inject()!"); | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ok restricting it to
1.4as long as it doesn't try to inadvertently instrument any unsupported classes. We ensure this by including the belowverifyPresentclasses in theclassLoaderHasClasseslist in the instrumentation.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this is where it gets complex! The reason 1.3 isn't supported is because the interfaces have different methods, they are the same classes but method signatures changed. Specifically it is the
RequestSpecthat has the changes. I could rely on a class that is only in 1.4 to force this, but one that isn't instrumentedThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that is ok. The classes in that list aren't the ones we're explicitly instrumenting. They're intended as a "fingerprint" to identify the supported versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've sorted out the fingerprint now. It is pretty much just that method on that class that identifies 1.4 from 1.3