-
Notifications
You must be signed in to change notification settings - Fork 349
Fix message handler spans appear disconnected from the incoming SQS trace #11511
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
Changes from all commits
0cf1997
2fffa01
a201b02
f4c80bc
7a169cf
c956f93
1b040bd
37361f3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| package datadog.trace.instrumentation.aws.v2.sqs; | ||
|
|
||
| import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; | ||
| import static net.bytebuddy.matcher.ElementMatchers.isMethod; | ||
|
|
||
| import datadog.trace.agent.tooling.Instrumenter; | ||
| import net.bytebuddy.asm.Advice; | ||
|
|
||
| public final class SqsMd5ChecksumInterceptorInstrumentation | ||
| implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice { | ||
|
|
||
| @Override | ||
| public String instrumentedType() { | ||
| // The AWS SDK checksum interceptor reads ReceiveMessageResponse.messages() while finalizing | ||
| // the response. Mark that internal access so we only wrap messages for application code. | ||
| return "software.amazon.awssdk.services.sqs.internal.MessageMD5ChecksumInterceptor"; | ||
| } | ||
|
|
||
| @Override | ||
| public void methodAdvice(MethodTransformer transformer) { | ||
| transformer.applyAdvice( | ||
| isMethod().and(named("afterExecution")), getClass().getName() + "$AfterExecutionAdvice"); | ||
| } | ||
|
|
||
| public static class AfterExecutionAdvice { | ||
| @Advice.OnMethodEnter(suppress = Throwable.class) | ||
| public static void onEnter() { | ||
| SqsReceiveResponseInternalAccess.enter(); | ||
|
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. don't we need to register SqsReceiveResponseInternalAccess as a helper for this advice ?
Contributor
Author
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. Correct. It's registered in the module: https://github.com/DataDog/dd-trace-java/pull/11511/changes#diff-11edc0f6db02d9c1bf0fcfb56354d561b9ee7b9cf8e69398c36642bc9e852541R26 |
||
| } | ||
|
|
||
| @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) | ||
| public static void onExit() { | ||
| SqsReceiveResponseInternalAccess.exit(); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| package datadog.trace.instrumentation.aws.v2.sqs; | ||
|
|
||
| import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; | ||
| import static net.bytebuddy.matcher.ElementMatchers.isMethod; | ||
| import static net.bytebuddy.matcher.ElementMatchers.returns; | ||
| import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; | ||
|
|
||
| import datadog.trace.agent.tooling.Instrumenter; | ||
| import datadog.trace.bootstrap.ContextStore; | ||
| import datadog.trace.bootstrap.InstrumentationContext; | ||
| import net.bytebuddy.asm.Advice; | ||
| import software.amazon.awssdk.services.sqs.model.ReceiveMessageResponse; | ||
|
|
||
| public final class SqsReceiveResponseBuilderImplInstrumentation | ||
| implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice { | ||
|
|
||
| private static final String BUILDER_IMPL = | ||
| "software.amazon.awssdk.services.sqs.model.ReceiveMessageResponse$BuilderImpl"; | ||
|
|
||
| @Override | ||
| public String instrumentedType() { | ||
| return BUILDER_IMPL; | ||
| } | ||
|
|
||
| @Override | ||
| public void methodAdvice(MethodTransformer transformer) { | ||
| transformer.applyAdvice( | ||
| isMethod() | ||
| .and(named("build")) | ||
| .and(takesNoArguments()) | ||
| .and( | ||
| returns(named("software.amazon.awssdk.services.sqs.model.ReceiveMessageResponse"))), | ||
| getClass().getName() + "$BuildAdvice"); | ||
| } | ||
|
|
||
| public static class BuildAdvice { | ||
| @Advice.OnMethodExit(suppress = Throwable.class) | ||
| public static void onExit( | ||
| @Advice.This Object builder, @Advice.Return ReceiveMessageResponse response) { | ||
| if (response == null) { | ||
| return; | ||
| } | ||
| ContextStore<Object, String> builderStore = | ||
| InstrumentationContext.get(BUILDER_IMPL, "java.lang.String"); | ||
| String queueUrl = builderStore.get(builder); | ||
| if (queueUrl != null) { | ||
| // Complete the handoff from the pre-rebuild response to the final response user code | ||
| // sees. | ||
| InstrumentationContext.get(ReceiveMessageResponse.class, String.class) | ||
| .put(response, queueUrl); | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| package datadog.trace.instrumentation.aws.v2.sqs; | ||
|
|
||
| import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; | ||
| import static net.bytebuddy.matcher.ElementMatchers.isMethod; | ||
| import static net.bytebuddy.matcher.ElementMatchers.returns; | ||
| import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; | ||
|
|
||
| import datadog.trace.agent.tooling.Instrumenter; | ||
| import datadog.trace.bootstrap.InstrumentationContext; | ||
| import net.bytebuddy.asm.Advice; | ||
| import software.amazon.awssdk.services.sqs.model.ReceiveMessageResponse; | ||
|
|
||
| public final class SqsReceiveResponseBuilderInstrumentation | ||
| implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice { | ||
|
|
||
| private static final String BUILDER_IMPL = | ||
| "software.amazon.awssdk.services.sqs.model.ReceiveMessageResponse$BuilderImpl"; | ||
|
|
||
| @Override | ||
| public String instrumentedType() { | ||
| return "software.amazon.awssdk.services.sqs.model.ReceiveMessageResponse"; | ||
| } | ||
|
|
||
| @Override | ||
| public void methodAdvice(MethodTransformer transformer) { | ||
| transformer.applyAdvice( | ||
| isMethod() | ||
| .and(named("toBuilder")) | ||
| .and(takesNoArguments()) | ||
| .and( | ||
| returns( | ||
| named( | ||
| "software.amazon.awssdk.services.sqs.model.ReceiveMessageResponse$Builder"))), | ||
| getClass().getName() + "$ToBuilderAdvice"); | ||
| } | ||
|
|
||
| public static class ToBuilderAdvice { | ||
| @Advice.OnMethodExit(suppress = Throwable.class) | ||
| public static void onExit( | ||
| @Advice.This ReceiveMessageResponse response, @Advice.Return Object builder) { | ||
| if (builder == null) { | ||
| return; | ||
| } | ||
| String queueUrl = | ||
| InstrumentationContext.get(ReceiveMessageResponse.class, String.class).get(response); | ||
| if (queueUrl != null) { | ||
| // AWS SDK core finalizes modeled responses through response.toBuilder().build(). | ||
| // Carry queueUrl onto the builder so it survives that response instance change. | ||
| InstrumentationContext.get(BUILDER_IMPL, "java.lang.String").put(builder, queueUrl); | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package datadog.trace.instrumentation.aws.v2.sqs; | ||
|
|
||
| import datadog.trace.bootstrap.CallDepthThreadLocalMap; | ||
|
|
||
| public final class SqsReceiveResponseInternalAccess { | ||
|
|
||
| private SqsReceiveResponseInternalAccess() {} | ||
|
|
||
| public static void enter() { | ||
| CallDepthThreadLocalMap.incrementCallDepth(SqsReceiveResponseInternalAccess.class); | ||
| } | ||
|
|
||
| public static void exit() { | ||
| CallDepthThreadLocalMap.decrementCallDepth(SqsReceiveResponseInternalAccess.class); | ||
| } | ||
|
|
||
| public static boolean active() { | ||
| return CallDepthThreadLocalMap.getCallDepth(SqsReceiveResponseInternalAccess.class) > 0; | ||
| } | ||
| } |
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 think a comment why we need to instrument this internal class would be nice
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.
Yep, this is part of the commit message. It's needed to avoid wrapping messages during the SDK's internal MessageMD5ChecksumInterceptor pass to prevent creating consumer spans
before user code actually consumes the messages.
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.
yeah but when reading the code (in the editor, after merging), you don't go and look at the commit messages in the blame to understand what's going on, which is why I think having a comment in the code explaining things is better
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 agree with including this in the code; I just didn't want to run CI again just for a single comment line :)