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

Grizzly-http and grizzly-client instrumentation #1365

Merged
merged 39 commits into from
May 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
32928ec
set-up instrumentation module
heathkd Mar 31, 2020
b940c76
update decorate with request and response objects
heathkd Apr 8, 2020
1041484
initial HttpListener instrumentation
heathkd Apr 8, 2020
c08b1cc
initial HttpRequester instrumentation
heathkd Apr 8, 2020
6860ad0
create client span from mule http client
heathkd Apr 10, 2020
75e2c47
remove http listener instrumentation - this will be handled in server…
heathkd Apr 10, 2020
40f4c0f
reorganize and rename client instrumentation classes
heathkd Apr 10, 2020
0a4ad73
add server span instrumentation
heathkd Apr 10, 2020
86b83ec
change class to instrument to HttpServerFilter
heathkd Apr 11, 2020
544a7c6
experiment with creating the client span from HttpClientFilter
heathkd Apr 12, 2020
d2991d0
generalize client instrumentation and remove references to mule classes
heathkd Apr 14, 2020
641b1be
add initial tests for client and server instrumentation
heathkd Apr 16, 2020
2cbdbc5
create inject and extract adapter, and close server span after respon…
heathkd Apr 17, 2020
0696943
update client instrumentation and tests
heathkd Apr 19, 2020
8209648
update instrumentation and add executors to whitelist
heathkd Apr 20, 2020
29d70a1
change naming of server spans and update server tests
heathkd Apr 21, 2020
4de6394
change component name of client instrumentation to include grizzly
heathkd Apr 21, 2020
fdd6229
instrument HttpCodecFilter instead of HttpServerFilter for server ins…
heathkd Apr 23, 2020
970835f
remove unneeded executors from whitelist
heathkd Apr 24, 2020
e116368
rename test classes to reflect the grizzly components they are testing
heathkd Apr 24, 2020
63ad066
apply google java format
richardstartin Apr 27, 2020
1e37c56
rawtypes and dead code
richardstartin Apr 27, 2020
904a4d0
complete client instrumentation
richardstartin Apr 29, 2020
eec2d1b
add overload of activateSpan defaulting to not finishing on close
richardstartin Apr 29, 2020
380d611
apply code formatting
richardstartin Apr 29, 2020
2f7fc6e
generify pair
richardstartin Apr 29, 2020
9195a81
ensure a scope is created for the profiler
richardstartin Apr 29, 2020
9945a4f
parent to child link working
richardstartin Apr 30, 2020
9f333b6
handle error traces and fix tests
richardstartin Apr 30, 2020
399e80d
prepare for merge
richardstartin Apr 30, 2020
10dca37
Merge branch 'master' into heather.dsouza/mulesoft-instrumentation
richardstartin Apr 30, 2020
3c9d105
post merge fix
richardstartin Apr 30, 2020
24e9ab2
address basic comments before moving modules
richardstartin Apr 30, 2020
28c4454
rename filterchain instrumentation
richardstartin May 1, 2020
0ed2f02
group filterchain and server, remove ContextAttributes
richardstartin May 1, 2020
154ae82
Merge branch 'master' into heather.dsouza/mulesoft-instrumentation
richardstartin May 5, 2020
dc170af
Merge branch 'master' into heather.dsouza/mulesoft-instrumentation
richardstartin May 14, 2020
4b9af37
address review comments
richardstartin May 14, 2020
a802476
clarify grizzly-client version mismatch
richardstartin May 15, 2020
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
Expand Up @@ -13,6 +13,8 @@ private InstrumentationContext() {}
* <p>However, the implementation is actually provided by bytecode transformation for performance
* reasons.
*
* <p>This method must only be called within an Advice class.
*
* @param keyClass The key class context is attached to.
* @param contextClass The context class attached to the user class.
* @param <K> key class
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package datadog.trace.bootstrap.instrumentation.api;

public final class Pair<T, U> {

public static <T, U> Pair<T, U> of(T left, U right) {
return new Pair<>(left, right);
}

private final T left;
private final U right;

Pair(T left, U right) {
this.left = left;
this.right = right;
}

public T getLeft() {
return left;
}

public U getRight() {
return right;
}

public boolean hasLeft() {
return null != left;
}

public boolean hasRight() {
return null != right;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
@Slf4j
public abstract class HttpServerDecorator<REQUEST, CONNECTION, RESPONSE> extends ServerDecorator {
public static final String DD_SPAN_ATTRIBUTE = "datadog.span";
public static final String DD_RESPONSE_ATTRIBUTE = "datadog.response";

// Source: https://www.regextester.com/22
private static final Pattern VALID_IPV4_ADDRESS =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static datadog.trace.bootstrap.instrumentation.api.DDSpanNames.GRIZZLY_REQUEST;
import static datadog.trace.bootstrap.instrumentation.decorator.HttpServerDecorator.DD_SPAN_ATTRIBUTE;
import static datadog.trace.instrumentation.grizzly.GrizzlyDecorator.DECORATE;
import static datadog.trace.instrumentation.grizzly.GrizzlyRequestExtractAdapter.GETTER;
Expand Down Expand Up @@ -71,7 +72,7 @@ public static AgentScope methodEnter(@Advice.Argument(0) final Request request)
}

final Context parentContext = propagate().extract(request, GETTER);
final AgentSpan span = startSpan("grizzly.request", parentContext);
final AgentSpan span = startSpan(GRIZZLY_REQUEST, parentContext);
DECORATE.afterStart(span);
DECORATE.onConnection(span, request);
DECORATE.onRequest(span, request);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import datadog.trace.agent.test.base.HttpServerTest
import datadog.trace.bootstrap.instrumentation.api.DDSpanNames
import datadog.trace.instrumentation.grizzly.GrizzlyDecorator
import org.glassfish.grizzly.http.server.HttpServer
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory
Expand Down Expand Up @@ -43,7 +44,7 @@ class GrizzlyTest extends HttpServerTest<HttpServer> {

@Override
String expectedOperationName() {
return "grizzly.request"
return DDSpanNames.GRIZZLY_REQUEST
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you want a static import here

}

static class SimpleExceptionMapper implements ExceptionMapper<Throwable> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
ext {
minJavaVersionForTests = JavaVersion.VERSION_1_8
}

muzzle {
pass {
group = "org.glassfish.grizzly"
module = "grizzly-http-client"
versions = "[1.9,1.16]"
assertInverse = true
}
pass {
group = "com.ning"
module = "async-http-client"
versions = "[1.9.0,)"
assertInverse = true
}
}

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

apply plugin: 'org.unbroken-dome.test-sets'

testSets {
latestDepTest {
dirName = 'test'
}
}

dependencies {
compileOnly group: 'org.glassfish.grizzly', name: 'grizzly-http-client', version: '1.9'
// for some reason, the tests don't *load* until 1.12, but muzzles works as far back as 1.9
testCompile group: 'org.glassfish.grizzly', name: 'grizzly-http-client', version: '1.12'

latestDepTestCompile group: 'org.glassfish.grizzly', name: 'grizzly-http-client', version: '1.16'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package datadog.trace.instrumentation.grizzly.client;

import static datadog.trace.bootstrap.instrumentation.api.DDComponents.GRIZZLY_HTTP_ASYNC_CLIENT;

import com.ning.http.client.Request;
import com.ning.http.client.Response;
import datadog.trace.bootstrap.instrumentation.decorator.HttpClientDecorator;
import java.net.URI;
import java.net.URISyntaxException;

public class ClientDecorator extends HttpClientDecorator<Request, Response> {

public static final ClientDecorator DECORATE = new ClientDecorator();

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

@Override
protected String component() {
return GRIZZLY_HTTP_ASYNC_CLIENT;
}

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

@Override
protected URI url(final Request request) throws URISyntaxException {
return request.getUri().toJavaNetURI();
}

@Override
protected Integer status(final Response response) {
return response.getStatusCode();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package datadog.trace.instrumentation.grizzly.client;

import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static datadog.trace.bootstrap.instrumentation.api.DDSpanNames.HTTP_REQUEST;
import static datadog.trace.instrumentation.grizzly.client.ClientDecorator.DECORATE;
import static datadog.trace.instrumentation.grizzly.client.InjectAdapter.SETTER;

import com.ning.http.client.AsyncHandler;
import com.ning.http.client.Request;
import datadog.trace.bootstrap.InstrumentationContext;
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.Pair;
import net.bytebuddy.asm.Advice;

public class ClientRequestAdvice {

@Advice.OnMethodEnter(suppress = Throwable.class)
public static AgentScope onEnter(
@Advice.Argument(0) final Request request,
@Advice.Argument(1) final AsyncHandler<?> handler) {
AgentSpan parentSpan = activeSpan();
AgentSpan span = startSpan(HTTP_REQUEST);
DECORATE.afterStart(span);
DECORATE.onRequest(span, request);
propagate().inject(span, request, SETTER);
InstrumentationContext.get(AsyncHandler.class, Pair.class)
.put(handler, Pair.of(parentSpan, span));
return activateSpan(span);
}

@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(@Advice.Enter final AgentScope scope) {
// span closed in ClientResponseAdvice, scope only created for profiler's benefit
scope.close();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package datadog.trace.instrumentation.grizzly.client;

import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.bootstrap.instrumentation.api.Pair;
import java.util.Map;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

@AutoService(Instrumenter.class)
public final class ClientRequestInstrumentation extends Instrumenter.Default {

public ClientRequestInstrumentation() {
super("grizzly-client", "ning");
}

@Override
public Map<String, String> contextStore() {
return singletonMap("com.ning.http.client.AsyncHandler", Pair.class.getName());
}

@Override
public ElementMatcher<? super TypeDescription> typeMatcher() {
return named("com.ning.http.client.AsyncHttpClient");
}

@Override
public String[] helperClassNames() {
return new String[] {packageName + ".ClientDecorator", packageName + ".InjectAdapter"};
}

@Override
protected boolean defaultEnabled() {
return false;
}

@Override
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
return singletonMap(
named("executeRequest")
.and(takesArgument(0, named("com.ning.http.client.Request")))
.and(takesArgument(1, named("com.ning.http.client.AsyncHandler")))
.and(isPublic()),
packageName + ".ClientRequestAdvice");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package datadog.trace.instrumentation.grizzly.client;

import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.instrumentation.grizzly.client.ClientDecorator.DECORATE;

import com.ning.http.client.AsyncCompletionHandler;
import com.ning.http.client.AsyncHandler;
import com.ning.http.client.Response;
import datadog.trace.bootstrap.ContextStore;
import datadog.trace.bootstrap.InstrumentationContext;
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.Pair;
import net.bytebuddy.asm.Advice;

@SuppressWarnings({"unchecked", "rawtypes"})
public class ClientResponseAdvice {

@Advice.OnMethodEnter(suppress = Throwable.class)
public static AgentScope onEnter(
@Advice.This final AsyncCompletionHandler<?> handler,
@Advice.Argument(0) final Response response) {
ContextStore<AsyncHandler, Pair> contextStore =
InstrumentationContext.get(AsyncHandler.class, Pair.class);
Pair<AgentSpan, AgentSpan> spanWithParent = contextStore.get(handler);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's easy to lose track of what's left and what's right below... consider assigning them to more descriptive local variables early.

if (null != spanWithParent) {
contextStore.put(handler, null);
}
if (spanWithParent.hasRight()) {
DECORATE.onResponse(spanWithParent.getRight(), response);
DECORATE.beforeFinish(spanWithParent.getRight());
spanWithParent.getRight().finish();
}
return spanWithParent.hasLeft() ? activateSpan(spanWithParent.getLeft()) : null;
}

@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(@Advice.Enter final AgentScope scope) {
if (null != scope) {
scope.close();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package datadog.trace.instrumentation.grizzly.client;

import static datadog.trace.agent.tooling.ClassLoaderMatcher.hasClassesNamed;
import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperClass;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.bootstrap.instrumentation.api.Pair;
import java.util.Map;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

@AutoService(Instrumenter.class)
public final class ClientResponseInstrumentation extends Instrumenter.Default {

public ClientResponseInstrumentation() {
super("grizzly-client", "ning");
}

@Override
public Map<String, String> contextStore() {
return singletonMap("com.ning.http.client.AsyncHandler", Pair.class.getName());
}

@Override
public ElementMatcher<ClassLoader> classLoaderMatcher() {
return hasClassesNamed("com.ning.http.client.AsyncCompletionHandler");
}

@Override
protected boolean defaultEnabled() {
return false;
}

@Override
public ElementMatcher<? super TypeDescription> typeMatcher() {
return hasSuperClass(named("com.ning.http.client.AsyncCompletionHandler"));
}

@Override
public String[] helperClassNames() {
return new String[] {packageName + ".ClientDecorator"};
}

@Override
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
return singletonMap(
named("onCompleted")
.and(takesArgument(0, named("com.ning.http.client.Response")))
.and(isPublic()),
packageName + ".ClientResponseAdvice");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package datadog.trace.instrumentation.grizzly.client;

import com.ning.http.client.Request;
import datadog.trace.bootstrap.instrumentation.api.AgentPropagation;

public class InjectAdapter implements AgentPropagation.Setter<Request> {

public static final InjectAdapter SETTER = new InjectAdapter();

@Override
public void set(final Request carrier, final String key, final String value) {
carrier.getHeaders().replaceWith(key, value);
}
}
Loading