From 871a6f2ecd30351b8fff739aba7bebe35636a344 Mon Sep 17 00:00:00 2001 From: Valentin Zakharov Date: Thu, 29 Jan 2026 23:42:34 +0100 Subject: [PATCH 1/5] Fix Synapse passthru spans finishing before status code is available --- .../SynapseClientInstrumentation.java | 30 ++++++++++++++----- .../SynapseServerInstrumentation.java | 28 +++++++++++++---- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseClientInstrumentation.java b/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseClientInstrumentation.java index 19ca1eaab8d..743c5374ddc 100644 --- a/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseClientInstrumentation.java +++ b/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseClientInstrumentation.java @@ -21,6 +21,7 @@ import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import net.bytebuddy.asm.Advice; import org.apache.axis2.context.MessageContext; +import org.apache.http.HttpResponse; import org.apache.http.nio.NHttpClientConnection; import org.apache.synapse.transport.passthru.TargetContext; @@ -114,7 +115,7 @@ public static final class ClientResponseAdvice { public static ContextScope beginResponse( @Advice.Argument(0) final NHttpClientConnection connection) { // check and remove context so it won't be finished twice - Context context = (Context) connection.getContext().removeAttribute(SYNAPSE_CONTEXT_KEY); + Context context = (Context) connection.getContext().getAttribute(SYNAPSE_CONTEXT_KEY); if (null != context) { return context.attach(); } @@ -129,15 +130,30 @@ public static void responseReceived( if (null == scope) { return; } - AgentSpan span = spanFromContext(scope.context()); - DECORATE.onResponse(span, connection.getHttpResponse()); + final AgentSpan span = spanFromContext(scope.context()); + final HttpResponse httpResponse = connection.getHttpResponse(); + boolean isFinal = false; + if (httpResponse != null && httpResponse.getStatusLine() != null) { + int statusCode = httpResponse.getStatusLine().getStatusCode(); + // 1xx (e.g. 100 Continue) - not final response + isFinal = statusCode >= 200; + } + + if (isFinal) { + DECORATE.onResponse(span, httpResponse); + } if (null != error) { DECORATE.onError(span, error); } - DECORATE.beforeFinish(scope.context()); - scope.close(); - if (span != null) { - span.finish(); + if ((isFinal || error != null) + && connection.getContext().removeAttribute(SYNAPSE_CONTEXT_KEY) != null) { + DECORATE.beforeFinish(scope.context()); + scope.close(); + if (span != null) { + span.finish(); + } + } else { + scope.close(); } } } diff --git a/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseServerInstrumentation.java b/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseServerInstrumentation.java index 6bd0122e3c0..ca59013aa9c 100644 --- a/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseServerInstrumentation.java +++ b/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseServerInstrumentation.java @@ -17,6 +17,7 @@ import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import net.bytebuddy.asm.Advice; import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; import org.apache.http.nio.NHttpServerConnection; @AutoService(InstrumenterModule.class) @@ -39,6 +40,7 @@ public String[] helperClassNames() { packageName + ".ExtractAdapter$Request", packageName + ".ExtractAdapter$Response", packageName + ".SynapseServerDecorator", + packageName + ".SynapseServerInstrumentation$ServerResponseAdvice", }; } @@ -93,7 +95,7 @@ public static final class ServerResponseAdvice { public static ContextScope beginResponse( @Advice.Argument(0) final NHttpServerConnection connection) { // check and remove context so it won't be finished twice - Context context = (Context) connection.getContext().removeAttribute(SYNAPSE_CONTEXT_KEY); + Context context = (Context) connection.getContext().getAttribute(SYNAPSE_CONTEXT_KEY); if (null != context) { return context.attach(); } @@ -108,14 +110,28 @@ public static void responseReady( if (null == scope) { return; } - AgentSpan span = spanFromContext(scope.context()); - DECORATE.onResponse(span, connection.getHttpResponse()); + final AgentSpan span = spanFromContext(scope.context()); + final HttpResponse httpResponse = connection.getHttpResponse(); + boolean isFinal = false; + if (httpResponse != null && httpResponse.getStatusLine() != null) { + isFinal = httpResponse.getStatusLine().getStatusCode() >= 200; + } + + if (isFinal) { + DECORATE.onResponse(span, httpResponse); + } if (null != error) { DECORATE.onError(span, error); } - DECORATE.beforeFinish(scope.context()); - scope.close(); - span.finish(); + + if ((isFinal || error != null) + && connection.getContext().removeAttribute(SYNAPSE_CONTEXT_KEY) != null) { + DECORATE.beforeFinish(scope.context()); + scope.close(); + span.finish(); + } else { + scope.close(); + } } } From 33ad9027ae5f87d2d517aff012a8d744bcab5e43 Mon Sep 17 00:00:00 2001 From: Valentin Zakharov Date: Fri, 30 Jan 2026 15:57:02 +0100 Subject: [PATCH 2/5] Fix for Muzzle check --- .../instrumentation/synapse3/SynapseServerInstrumentation.java | 1 - 1 file changed, 1 deletion(-) diff --git a/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseServerInstrumentation.java b/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseServerInstrumentation.java index ca59013aa9c..8eb24acd3de 100644 --- a/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseServerInstrumentation.java +++ b/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseServerInstrumentation.java @@ -40,7 +40,6 @@ public String[] helperClassNames() { packageName + ".ExtractAdapter$Request", packageName + ".ExtractAdapter$Response", packageName + ".SynapseServerDecorator", - packageName + ".SynapseServerInstrumentation$ServerResponseAdvice", }; } From 75f4a4f7efb371e85d4b9673f62723a09ebf2504 Mon Sep 17 00:00:00 2001 From: Valentin Zakharov Date: Fri, 30 Jan 2026 17:39:09 +0100 Subject: [PATCH 3/5] Dummy commit --- .../datadog/trace/instrumentation/synapse3/SynapseTest.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dd-java-agent/instrumentation/synapse-3.0/src/test/groovy/datadog/trace/instrumentation/synapse3/SynapseTest.groovy b/dd-java-agent/instrumentation/synapse-3.0/src/test/groovy/datadog/trace/instrumentation/synapse3/SynapseTest.groovy index caf7be64d54..151390d36bc 100644 --- a/dd-java-agent/instrumentation/synapse-3.0/src/test/groovy/datadog/trace/instrumentation/synapse3/SynapseTest.groovy +++ b/dd-java-agent/instrumentation/synapse-3.0/src/test/groovy/datadog/trace/instrumentation/synapse3/SynapseTest.groovy @@ -26,6 +26,7 @@ import org.apache.synapse.ServerConfigurationInformation import org.apache.synapse.ServerContextInformation import org.apache.synapse.ServerManager import org.apache.synapse.transport.passthru.PassThroughHttpListener +import spock.lang.IgnoreRest import spock.lang.Shared import java.lang.reflect.Field @@ -148,6 +149,7 @@ abstract class SynapseTest extends VersionedNamingTestBase { statusCode == 200 } + //@IgnoreRest def "test passthru request is traced"() { setup: def request = new Request.Builder() @@ -198,6 +200,7 @@ abstract class SynapseTest extends VersionedNamingTestBase { statusCode == 500 } + //@IgnoreRest def "test client request is traced"() { setup: def request = new Request.Builder() From 7bdce96bb5cb9b914d2e144ba584955da5e65be4 Mon Sep 17 00:00:00 2001 From: Valentin Zakharov Date: Fri, 30 Jan 2026 18:37:54 +0100 Subject: [PATCH 4/5] Clean test --- .../datadog/trace/instrumentation/synapse3/SynapseTest.groovy | 3 --- 1 file changed, 3 deletions(-) diff --git a/dd-java-agent/instrumentation/synapse-3.0/src/test/groovy/datadog/trace/instrumentation/synapse3/SynapseTest.groovy b/dd-java-agent/instrumentation/synapse-3.0/src/test/groovy/datadog/trace/instrumentation/synapse3/SynapseTest.groovy index 151390d36bc..caf7be64d54 100644 --- a/dd-java-agent/instrumentation/synapse-3.0/src/test/groovy/datadog/trace/instrumentation/synapse3/SynapseTest.groovy +++ b/dd-java-agent/instrumentation/synapse-3.0/src/test/groovy/datadog/trace/instrumentation/synapse3/SynapseTest.groovy @@ -26,7 +26,6 @@ import org.apache.synapse.ServerConfigurationInformation import org.apache.synapse.ServerContextInformation import org.apache.synapse.ServerManager import org.apache.synapse.transport.passthru.PassThroughHttpListener -import spock.lang.IgnoreRest import spock.lang.Shared import java.lang.reflect.Field @@ -149,7 +148,6 @@ abstract class SynapseTest extends VersionedNamingTestBase { statusCode == 200 } - //@IgnoreRest def "test passthru request is traced"() { setup: def request = new Request.Builder() @@ -200,7 +198,6 @@ abstract class SynapseTest extends VersionedNamingTestBase { statusCode == 500 } - //@IgnoreRest def "test client request is traced"() { setup: def request = new Request.Builder() From 3f18bf9da6300eb31c010d2e48ace1a041cf921b Mon Sep 17 00:00:00 2001 From: Valentin Zakharov Date: Sat, 31 Jan 2026 15:02:41 +0100 Subject: [PATCH 5/5] Updated comments --- .../instrumentation/synapse3/SynapseClientInstrumentation.java | 2 +- .../instrumentation/synapse3/SynapseServerInstrumentation.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseClientInstrumentation.java b/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseClientInstrumentation.java index 743c5374ddc..dd57c58c552 100644 --- a/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseClientInstrumentation.java +++ b/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseClientInstrumentation.java @@ -114,7 +114,7 @@ public static final class ClientResponseAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static ContextScope beginResponse( @Advice.Argument(0) final NHttpClientConnection connection) { - // check and remove context so it won't be finished twice + // don't remove stored context here because the response callback may run multiple times Context context = (Context) connection.getContext().getAttribute(SYNAPSE_CONTEXT_KEY); if (null != context) { return context.attach(); diff --git a/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseServerInstrumentation.java b/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseServerInstrumentation.java index 8eb24acd3de..d16e628121c 100644 --- a/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseServerInstrumentation.java +++ b/dd-java-agent/instrumentation/synapse-3.0/src/main/java/datadog/trace/instrumentation/synapse3/SynapseServerInstrumentation.java @@ -93,7 +93,7 @@ public static final class ServerResponseAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static ContextScope beginResponse( @Advice.Argument(0) final NHttpServerConnection connection) { - // check and remove context so it won't be finished twice + // don't remove stored context here because the response callback may run multiple times Context context = (Context) connection.getContext().getAttribute(SYNAPSE_CONTEXT_KEY); if (null != context) { return context.attach();