Skip to content

Commit 5512176

Browse files
authored
[Instrumentation.AWS]: always add context propagation data to requests (open-telemetry#2447)
1 parent fbba3f8 commit 5512176

File tree

3 files changed

+96
-17
lines changed

3 files changed

+96
-17
lines changed

src/OpenTelemetry.Instrumentation.AWS/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## Unreleased
44

5+
* Context propagation data is always added to SQS and SNS requests regardless of
6+
sampling decision. This enables downstream services to make consistent sampling
7+
decisions and prevents incomplete traces.
8+
([#2447](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2447))
9+
510
## 1.10.0-rc.1
611

712
Released 2025-Jan-06

src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSTracingPipelineHandler.cs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,22 @@ public override async Task<T> InvokeAsync<T>(IExecutionContext executionContext)
4646
return ret;
4747
}
4848

49+
private static void AddPropagationDataToRequest(Activity activity, IRequestContext requestContext)
50+
{
51+
var service = requestContext.ServiceMetaData.ServiceId;
52+
53+
if (AWSServiceType.IsSqsService(service))
54+
{
55+
SqsRequestContextHelper.AddAttributes(
56+
requestContext, AWSMessagingUtils.InjectIntoDictionary(new PropagationContext(activity.Context, Baggage.Current)));
57+
}
58+
else if (AWSServiceType.IsSnsService(service))
59+
{
60+
SnsRequestContextHelper.AddAttributes(
61+
requestContext, AWSMessagingUtils.InjectIntoDictionary(new PropagationContext(activity.Context, Baggage.Current)));
62+
}
63+
}
64+
4965
#if NET
5066
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage(
5167
"Trimming",
@@ -167,16 +183,6 @@ private void AddRequestSpecificInformation(Activity activity, IRequestContext re
167183
{
168184
this.awsSemanticConventions.TagBuilder.SetTagAttributeDbSystemToDynamoDb(activity);
169185
}
170-
else if (AWSServiceType.IsSqsService(service))
171-
{
172-
SqsRequestContextHelper.AddAttributes(
173-
requestContext, AWSMessagingUtils.InjectIntoDictionary(new PropagationContext(activity.Context, Baggage.Current)));
174-
}
175-
else if (AWSServiceType.IsSnsService(service))
176-
{
177-
SnsRequestContextHelper.AddAttributes(
178-
requestContext, AWSMessagingUtils.InjectIntoDictionary(new PropagationContext(activity.Context, Baggage.Current)));
179-
}
180186
else if (AWSServiceType.IsBedrockRuntimeService(service))
181187
{
182188
this.awsSemanticConventions.TagBuilder.SetTagAttributeGenAiSystemToBedrock(activity);
@@ -202,14 +208,21 @@ private void ProcessEndRequest(Activity? activity, IExecutionContext executionCo
202208

203209
var currentActivity = Activity.Current;
204210

205-
if (currentActivity == null
206-
|| !currentActivity.Source.Name.StartsWith(TelemetryConstants.TelemetryScopePrefix, StringComparison.Ordinal)
207-
|| !currentActivity.IsAllDataRequested)
211+
if (currentActivity == null)
208212
{
209213
return null;
210214
}
211215

212-
this.AddRequestSpecificInformation(currentActivity, executionContext.RequestContext);
216+
if (currentActivity.IsAllDataRequested
217+
&& currentActivity.Source.Name.StartsWith(TelemetryConstants.TelemetryScopePrefix, StringComparison.Ordinal))
218+
{
219+
this.AddRequestSpecificInformation(currentActivity, executionContext.RequestContext);
220+
}
221+
222+
// Context propagation should always happen regardless of sampling decision (which affects Activity.IsAllDataRequested and Activity.Source).
223+
// Otherwise, downstream services can make inconsistent sampling decisions and create incomplete traces.
224+
AddPropagationDataToRequest(currentActivity, executionContext.RequestContext);
225+
213226
return currentActivity;
214227
}
215228
}

test/OpenTelemetry.Instrumentation.AWS.Tests/TestAWSClientInstrumentation.cs

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,16 +185,18 @@ public async Task TestDDBScanUnsuccessful()
185185

186186
[Fact]
187187
#if NETFRAMEWORK
188-
public void TestSQSSendMessageSuccessful()
188+
public void TestSQSSendMessageSuccessfulSampled()
189189
#else
190-
public async Task TestSQSSendMessageSuccessful()
190+
public async Task TestSQSSendMessageSuccessfulSampled()
191191
#endif
192192
{
193193
var exportedItems = new List<Activity>();
194194

195195
var parent = new Activity("parent").Start();
196196
var requestId = @"fakerequ-esti-dfak-ereq-uestidfakere";
197197

198+
SendMessageRequest send_msg_req;
199+
198200
using (Sdk.CreateTracerProviderBuilder()
199201
.AddXRayTraceId()
200202
.SetSampler(new AlwaysOnSampler())
@@ -208,7 +210,7 @@ public async Task TestSQSSendMessageSuccessful()
208210
var sqs = new AmazonSQSClient(new AnonymousAWSCredentials(), RegionEndpoint.USEast1);
209211
var dummyResponse = "{}";
210212
CustomResponses.SetResponse(sqs, dummyResponse, requestId, true);
211-
var send_msg_req = new SendMessageRequest
213+
send_msg_req = new SendMessageRequest
212214
{
213215
QueueUrl = "https://sqs.us-east-1.amazonaws.com/123456789/MyTestQueue",
214216
MessageBody = "Hello from OT",
@@ -230,6 +232,65 @@ public async Task TestSQSSendMessageSuccessful()
230232

231233
Assert.Equal(ActivityStatusCode.Unset, awssdk_activity.Status);
232234
Assert.Equal(requestId, Utils.GetTagValue(awssdk_activity, "aws.request_id"));
235+
236+
Assert.Equal(2, send_msg_req.MessageAttributes.Count);
237+
Assert.Contains(
238+
send_msg_req.MessageAttributes,
239+
kv => kv.Key == "traceparent" && kv.Value.StringValue == $"00-{awssdk_activity.TraceId}-{awssdk_activity.SpanId}-01");
240+
Assert.Contains(
241+
send_msg_req.MessageAttributes,
242+
kv => kv.Key == "Custom" && kv.Value.StringValue == "Value");
243+
}
244+
245+
[Fact]
246+
#if NETFRAMEWORK
247+
public void TestSQSSendMessageSuccessfulNotSampled()
248+
#else
249+
public async Task TestSQSSendMessageSuccessfulNotSampled()
250+
#endif
251+
{
252+
var exportedItems = new List<Activity>();
253+
254+
var parent = new Activity("parent").Start();
255+
var requestId = @"fakerequ-esti-dfak-ereq-uestidfakere";
256+
257+
SendMessageRequest send_msg_req;
258+
259+
using (Sdk.CreateTracerProviderBuilder()
260+
.AddXRayTraceId()
261+
.SetSampler(new AlwaysOffSampler())
262+
.AddAWSInstrumentation(o =>
263+
{
264+
o.SemanticConventionVersion = SemanticConventionVersion.Latest;
265+
})
266+
.AddInMemoryExporter(exportedItems)
267+
.Build())
268+
{
269+
var sqs = new AmazonSQSClient(new AnonymousAWSCredentials(), RegionEndpoint.USEast1);
270+
var dummyResponse = "{}";
271+
CustomResponses.SetResponse(sqs, dummyResponse, requestId, true);
272+
send_msg_req = new SendMessageRequest
273+
{
274+
QueueUrl = "https://sqs.us-east-1.amazonaws.com/123456789/MyTestQueue",
275+
MessageBody = "Hello from OT",
276+
};
277+
send_msg_req.MessageAttributes.Add("Custom", new MessageAttributeValue { StringValue = "Value", DataType = "String" });
278+
#if NETFRAMEWORK
279+
sqs.SendMessage(send_msg_req);
280+
#else
281+
await sqs.SendMessageAsync(send_msg_req);
282+
#endif
283+
}
284+
285+
Assert.Empty(exportedItems);
286+
287+
Assert.Equal(2, send_msg_req.MessageAttributes.Count);
288+
Assert.Contains(
289+
send_msg_req.MessageAttributes,
290+
kv => kv.Key == "traceparent" && kv.Value.StringValue == $"00-{parent.TraceId}-{parent.SpanId}-00");
291+
Assert.Contains(
292+
send_msg_req.MessageAttributes,
293+
kv => kv.Key == "Custom" && kv.Value.StringValue == "Value");
233294
}
234295

235296
[Fact]

0 commit comments

Comments
 (0)