diff --git a/aws-xray-recorder-sdk-apache-http/src/main/java/com/amazonaws/xray/proxies/apache/http/TracedHttpClient.java b/aws-xray-recorder-sdk-apache-http/src/main/java/com/amazonaws/xray/proxies/apache/http/TracedHttpClient.java index 8d26734e..03d95c72 100644 --- a/aws-xray-recorder-sdk-apache-http/src/main/java/com/amazonaws/xray/proxies/apache/http/TracedHttpClient.java +++ b/aws-xray-recorder-sdk-apache-http/src/main/java/com/amazonaws/xray/proxies/apache/http/TracedHttpClient.java @@ -18,6 +18,7 @@ import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.AWSXRayRecorder; import com.amazonaws.xray.entities.Namespace; +import com.amazonaws.xray.entities.NoOpSubSegment; import com.amazonaws.xray.entities.Segment; import com.amazonaws.xray.entities.Subsegment; import com.amazonaws.xray.entities.TraceHeader; @@ -104,15 +105,15 @@ public static void addRequestInformation(Subsegment subsegment, HttpRequest requ Segment parentSegment = subsegment.getParentSegment(); if (subsegment.shouldPropagate()) { - // If the trace is not sampled, passing Parent and Sampled won't matter + // If no-op, only propagate root trace ID to not taint sampling decision TraceHeader t = TraceHeader.fromEntity(subsegment); - if (t.getSampled() != TraceHeader.SampleDecision.SAMPLED) { + if (subsegment instanceof NoOpSubSegment) { request.addHeader( TraceHeader.HEADER_KEY, "Root=" + t.getRootTraceId().toString()); } else { // This will propagate Parent and Sampled - request.addHeader(TraceHeader.HEADER_KEY, TraceHeader.fromEntity(subsegment).toString()); + request.addHeader(TraceHeader.HEADER_KEY, t.toString()); } } diff --git a/aws-xray-recorder-sdk-apache-http/src/test/java/com/amazonaws/xray/proxies/apache/http/TracedHttpClientTest.java b/aws-xray-recorder-sdk-apache-http/src/test/java/com/amazonaws/xray/proxies/apache/http/TracedHttpClientTest.java index 73b64a99..140279d6 100644 --- a/aws-xray-recorder-sdk-apache-http/src/test/java/com/amazonaws/xray/proxies/apache/http/TracedHttpClientTest.java +++ b/aws-xray-recorder-sdk-apache-http/src/test/java/com/amazonaws/xray/proxies/apache/http/TracedHttpClientTest.java @@ -114,7 +114,7 @@ public void unsampledButForcedPropagation() throws Exception { verify(getRequestedFor(urlPathEqualTo("/")) .withHeader(TraceHeader.HEADER_KEY, - equalTo("Root=1-67891233-abcdef012345678912345678"))); + equalTo("Root=1-67891233-abcdef012345678912345678;Sampled=0"))); } @Test diff --git a/aws-xray-recorder-sdk-aws-sdk-v2/src/main/java/com/amazonaws/xray/interceptors/TracingInterceptor.java b/aws-xray-recorder-sdk-aws-sdk-v2/src/main/java/com/amazonaws/xray/interceptors/TracingInterceptor.java index 26f1db9a..afc3b925 100644 --- a/aws-xray-recorder-sdk-aws-sdk-v2/src/main/java/com/amazonaws/xray/interceptors/TracingInterceptor.java +++ b/aws-xray-recorder-sdk-aws-sdk-v2/src/main/java/com/amazonaws/xray/interceptors/TracingInterceptor.java @@ -21,6 +21,7 @@ import com.amazonaws.xray.entities.EntityDataKeys; import com.amazonaws.xray.entities.EntityHeaderKeys; import com.amazonaws.xray.entities.Namespace; +import com.amazonaws.xray.entities.NoOpSubSegment; import com.amazonaws.xray.entities.Subsegment; import com.amazonaws.xray.entities.TraceHeader; import com.amazonaws.xray.handlers.config.AWSOperationHandler; @@ -296,9 +297,9 @@ public SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, Execu return httpRequest; } - // If the trace is not sampled, passing Parent and Sampled won't matter + // If no-op, only propagate root trace ID to not taint sampling decision TraceHeader t = TraceHeader.fromEntity(subsegment); - if (t.getSampled() != TraceHeader.SampleDecision.SAMPLED) { + if (subsegment instanceof NoOpSubSegment) { return httpRequest.toBuilder().putHeader( TraceHeader.HEADER_KEY, "Root=" + t.getRootTraceId().toString()).build(); @@ -307,7 +308,7 @@ public SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, Execu // This will propagate Parent and Sampled return httpRequest.toBuilder().putHeader( TraceHeader.HEADER_KEY, - TraceHeader.fromEntity(subsegment).toString()).build(); + t.toString()).build(); } @Override diff --git a/aws-xray-recorder-sdk-aws-sdk/src/main/java/com/amazonaws/xray/handlers/TracingHandler.java b/aws-xray-recorder-sdk-aws-sdk/src/main/java/com/amazonaws/xray/handlers/TracingHandler.java index c9e786cd..aacf0424 100644 --- a/aws-xray-recorder-sdk-aws-sdk/src/main/java/com/amazonaws/xray/handlers/TracingHandler.java +++ b/aws-xray-recorder-sdk-aws-sdk/src/main/java/com/amazonaws/xray/handlers/TracingHandler.java @@ -31,6 +31,7 @@ import com.amazonaws.xray.entities.EntityDataKeys; import com.amazonaws.xray.entities.EntityHeaderKeys; import com.amazonaws.xray.entities.Namespace; +import com.amazonaws.xray.entities.NoOpSubSegment; import com.amazonaws.xray.entities.Subsegment; import com.amazonaws.xray.entities.TraceHeader; import com.amazonaws.xray.handlers.config.AWSOperationHandler; @@ -192,15 +193,15 @@ public void beforeRequest(Request request) { currentSubsegment.setNamespace(Namespace.AWS.toString()); if (recorder.getCurrentSegment() != null && recorder.getCurrentSubsegment().shouldPropagate()) { - // If the trace is not sampled, passing Parent and Sampled won't matter + // If no-op, only propagate root trace ID to not taint sampling decision TraceHeader t = TraceHeader.fromEntity(currentSubsegment); - if (t.getSampled() != TraceHeader.SampleDecision.SAMPLED) { + if (currentSubsegment instanceof NoOpSubSegment) { request.addHeader( TraceHeader.HEADER_KEY, "Root=" + t.getRootTraceId().toString()); } else { // This will propagate Parent and Sampled - request.addHeader(TraceHeader.HEADER_KEY, TraceHeader.fromEntity(currentSubsegment).toString()); + request.addHeader(TraceHeader.HEADER_KEY, t.toString()); } } } diff --git a/aws-xray-recorder-sdk-core/src/main/java/com/amazonaws/xray/contexts/LambdaSegmentContext.java b/aws-xray-recorder-sdk-core/src/main/java/com/amazonaws/xray/contexts/LambdaSegmentContext.java index eded79fd..02c67e72 100644 --- a/aws-xray-recorder-sdk-core/src/main/java/com/amazonaws/xray/contexts/LambdaSegmentContext.java +++ b/aws-xray-recorder-sdk-core/src/main/java/com/amazonaws/xray/contexts/LambdaSegmentContext.java @@ -58,24 +58,20 @@ public Subsegment beginSubsegment(AWSXRayRecorder recorder, String name) { Entity entity = getTraceEntity(); if (entity == null) { // First subsegment of a subsegment branch Segment parentSegment; - // We rely on the AWS SDK to propagate one or both of these - // It is therefore possible to get just Root, or Root and Parent - if (traceHeader.getRootTraceId() != null) { - if (traceHeader.getParentId() != null) { - parentSegment = new FacadeSegment( - recorder, - traceHeader.getRootTraceId(), - traceHeader.getParentId(), - traceHeader.getSampled()); - } else { - if (logger.isDebugEnabled()) { - logger.debug("Creating No-Op parent segment"); - } - TraceID t = traceHeader.getRootTraceId(); - parentSegment = Segment.noOp(t, recorder); - } + // Trace header either takes the structure `Root=...;` or + // `Root=...;Parent=...;Sampled=...;` + if (traceHeader.getRootTraceId() != null && traceHeader.getParentId() != null && traceHeader.getSampled() != null) { + parentSegment = new FacadeSegment( + recorder, + traceHeader.getRootTraceId(), + traceHeader.getParentId(), + traceHeader.getSampled()); } else { - parentSegment = Segment.noOp(TraceID.create(recorder), recorder); + if (logger.isDebugEnabled()) { + logger.debug("Creating No-Op parent segment"); + } + TraceID t = traceHeader.getRootTraceId() != null ? traceHeader.getRootTraceId() : TraceID.create(recorder); + parentSegment = Segment.noOp(t, recorder); } boolean isRecording = parentSegment.isRecording(); diff --git a/aws-xray-recorder-sdk-core/src/main/java/com/amazonaws/xray/entities/NoOpSubSegment.java b/aws-xray-recorder-sdk-core/src/main/java/com/amazonaws/xray/entities/NoOpSubSegment.java index 37569279..8ef27881 100644 --- a/aws-xray-recorder-sdk-core/src/main/java/com/amazonaws/xray/entities/NoOpSubSegment.java +++ b/aws-xray-recorder-sdk-core/src/main/java/com/amazonaws/xray/entities/NoOpSubSegment.java @@ -25,7 +25,7 @@ import java.util.concurrent.locks.ReentrantLock; import org.checkerframework.checker.nullness.qual.Nullable; -class NoOpSubSegment implements Subsegment { +public class NoOpSubSegment implements Subsegment { private final Segment parentSegment; private final AWSXRayRecorder creator; private final boolean shouldPropagate;