-
Notifications
You must be signed in to change notification settings - Fork 292
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add MethodInvocation wrapper for spanning @async invocations
- Loading branch information
1 parent
d0f5f65
commit eae92f3
Showing
6 changed files
with
173 additions
and
45 deletions.
There are no files selected for viewing
This file contains 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
63 changes: 63 additions & 0 deletions
63
...src/main/java/datadog/trace/instrumentation/springscheduling/SpannedMethodInvocation.java
This file contains 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,63 @@ | ||
package datadog.trace.instrumentation.springscheduling; | ||
|
||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; | ||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; | ||
import static datadog.trace.instrumentation.springscheduling.SpringSchedulingDecorator.DECORATE; | ||
|
||
import datadog.trace.bootstrap.instrumentation.api.AgentScope; | ||
import datadog.trace.bootstrap.instrumentation.api.AgentSpan; | ||
import java.lang.reflect.AccessibleObject; | ||
import java.lang.reflect.Method; | ||
import org.aopalliance.intercept.MethodInvocation; | ||
|
||
public class SpannedMethodInvocation implements MethodInvocation { | ||
|
||
private final AgentSpan parent; | ||
private final MethodInvocation delegate; | ||
|
||
public SpannedMethodInvocation(AgentSpan parent, MethodInvocation delegate) { | ||
this.parent = parent; | ||
this.delegate = delegate; | ||
} | ||
|
||
@Override | ||
public Method getMethod() { | ||
return delegate.getMethod(); | ||
} | ||
|
||
@Override | ||
public Object[] getArguments() { | ||
return delegate.getArguments(); | ||
} | ||
|
||
@Override | ||
public Object proceed() throws Throwable { | ||
CharSequence spanName = DECORATE.spanNameForMethod(delegate.getMethod()); | ||
// TODO kill all APIs requiring String parameters with fire | ||
final AgentSpan span = | ||
parent == null | ||
? startSpan(spanName.toString()) | ||
: startSpan(spanName.toString(), parent.context()); | ||
try (AgentScope scope = activateSpan(span)) { | ||
// question: is this necessary? What does it do? | ||
// if the delegate does async work is everything OK because of this? | ||
// if the delegate does async work, should I need to worry about it here? | ||
scope.setAsyncPropagation(true); | ||
// do the actual work | ||
Object result = delegate.proceed(); | ||
// question? Why can't this just be AutoCloseable? Dogma? | ||
span.finish(); | ||
return result; | ||
} | ||
} | ||
|
||
@Override | ||
public Object getThis() { | ||
return delegate.getThis(); | ||
} | ||
|
||
@Override | ||
public AccessibleObject getStaticPart() { | ||
return delegate.getStaticPart(); | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
...g-3.1/src/main/java/datadog/trace/instrumentation/springscheduling/SpringAsyncAdvice.java
This file contains 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,16 @@ | ||
package datadog.trace.instrumentation.springscheduling; | ||
|
||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan; | ||
|
||
import net.bytebuddy.asm.Advice; | ||
import org.aopalliance.intercept.MethodInvocation; | ||
|
||
public class SpringAsyncAdvice { | ||
|
||
@Advice.OnMethodEnter(suppress = Throwable.class) | ||
public static void scheduleAsync( | ||
@Advice.Argument(value = 0, readOnly = false) MethodInvocation invocation) { | ||
// wrap so that when the invocation is invoked, it can be wrapped with a span's start and stop | ||
invocation = new SpannedMethodInvocation(activeSpan(), invocation); | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
.../main/java/datadog/trace/instrumentation/springscheduling/SpringAsyncInstrumentation.java
This file contains 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,38 @@ | ||
package datadog.trace.instrumentation.springscheduling; | ||
|
||
import static net.bytebuddy.matcher.ElementMatchers.isMethod; | ||
import static net.bytebuddy.matcher.ElementMatchers.named; | ||
|
||
import com.google.auto.service.AutoService; | ||
import datadog.trace.agent.tooling.Instrumenter; | ||
import java.util.Collections; | ||
import java.util.Map; | ||
import net.bytebuddy.description.method.MethodDescription; | ||
import net.bytebuddy.description.type.TypeDescription; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
import net.bytebuddy.matcher.ElementMatchers; | ||
|
||
@AutoService(Instrumenter.class) | ||
public class SpringAsyncInstrumentation extends Instrumenter.Default { | ||
|
||
public SpringAsyncInstrumentation() { | ||
super("spring-async"); | ||
} | ||
|
||
@Override | ||
public ElementMatcher<? super TypeDescription> typeMatcher() { | ||
return named("org.springframework.aop.interceptor.AsyncExecutionInterceptor"); | ||
} | ||
|
||
@Override | ||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() { | ||
return Collections.singletonMap( | ||
isMethod() | ||
.and( | ||
named("invoke") | ||
.and( | ||
ElementMatchers.takesArgument( | ||
0, named("org.aopalliance.intercept.MethodInvocation")))), | ||
packageName + ".SpringAsyncAdvice"); | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
dd-java-agent/instrumentation/spring-scheduling-3.1/src/test/groovy/SpringAsyncTest.groovy
This file contains 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,54 @@ | ||
import datadog.trace.agent.test.AgentTestRunner | ||
import datadog.trace.api.Trace | ||
import org.springframework.context.annotation.AnnotationConfigApplicationContext | ||
|
||
import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace | ||
|
||
class SpringAsyncTest extends AgentTestRunner { | ||
|
||
def "context propagated through @async annotation" () { | ||
setup: | ||
def context = new AnnotationConfigApplicationContext(AsyncTaskConfig) | ||
AsyncTask asyncTask = context.getBean(AsyncTask) | ||
when: | ||
runUnderTrace("root") { | ||
asyncTask.async() | ||
.thenApply({ | ||
new Leaf(it) | ||
}) | ||
.thenApplyAsync({it.leaf() }) | ||
.join() | ||
} | ||
then: | ||
assertTraces(1) { | ||
trace(0, 3) { | ||
span(0) { | ||
resourceName "root" | ||
threadNameStartsWith "main" | ||
} | ||
span(1) { | ||
resourceName "AsyncTask.async" | ||
childOf span(0) | ||
} | ||
span(2) { | ||
resourceName "Leaf.leaf" | ||
childOf span(1) | ||
} | ||
} | ||
} | ||
} | ||
|
||
static class Leaf { | ||
|
||
private int i | ||
|
||
Leaf(int i) { | ||
this.i = i | ||
} | ||
|
||
@Trace | ||
int leaf() { | ||
return i | ||
} | ||
} | ||
} |
This file contains 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