From 24fac231fcc6c816289ce17eee0bbc780547ead6 Mon Sep 17 00:00:00 2001 From: philip pittle Date: Mon, 5 Feb 2024 21:02:52 -0800 Subject: [PATCH 1/4] [Instrumentation.AWS] Use direct reference to activity source name --- .../TracerProviderBuilderExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Instrumentation.AWS/TracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.AWS/TracerProviderBuilderExtensions.cs index d522d4231f..c9e5c26cdd 100644 --- a/src/OpenTelemetry.Instrumentation.AWS/TracerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.AWS/TracerProviderBuilderExtensions.cs @@ -37,7 +37,7 @@ public static TracerProviderBuilder AddAWSInstrumentation( configure?.Invoke(awsClientOptions); _ = new AWSClientsInstrumentation(awsClientOptions); - builder.AddSource("Amazon.AWS.AWSClientInstrumentation"); + builder.AddSource(AWSTracingPipelineHandler.ActivitySourceName); return builder; } } From 880aca68d729dc0639da59a0f7f59e3e259651e1 Mon Sep 17 00:00:00 2001 From: philip pittle Date: Tue, 23 Jan 2024 21:05:23 -0800 Subject: [PATCH 2/4] [Instrumentation.AWS] Move AWSTracingPipelineCustomizer to activate earlier in the pipeline. This is necessary to support sqs becoming a json service and no longer supporting writing to MessageAttributes after Marshalling has occured. --- .../CHANGELOG.md | 3 + .../Implementation/AWSServiceHelper.cs | 2 +- .../Implementation/AWSServiceType.cs | 4 +- .../AWSTracingPipelineCustomizer.cs | 2 +- .../Implementation/SnsRequestContextHelper.cs | 20 +---- .../Implementation/SqsRequestContextHelper.cs | 20 +---- .../RequestContextHelperTests.cs | 87 ++++++++++--------- .../Implementation/TestsHelper.cs | 30 ++++--- .../TestAWSClientInstrumentation.cs | 5 +- .../TestRequest.cs | 8 +- 10 files changed, 82 insertions(+), 99 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AWS/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AWS/CHANGELOG.md index ea157ffcf4..f5c9d98b94 100644 --- a/src/OpenTelemetry.Instrumentation.AWS/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AWS/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +* BREAKING: Switched AWSServiceName tag to use ServiceId instead of ServiceName + ([#1572](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1572)) + ## 1.1.0-beta.3 Released 2024-Jan-26 diff --git a/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSServiceHelper.cs b/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSServiceHelper.cs index e940ba2f39..59661d6e4e 100644 --- a/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSServiceHelper.cs +++ b/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSServiceHelper.cs @@ -21,7 +21,7 @@ internal class AWSServiceHelper }; internal static string GetAWSServiceName(IRequestContext requestContext) - => Utils.RemoveAmazonPrefixFromServiceName(requestContext.Request.ServiceName); + => Utils.RemoveAmazonPrefixFromServiceName(requestContext.ServiceMetaData.ServiceId); internal static string GetAWSOperationName(IRequestContext requestContext) { diff --git a/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSServiceType.cs b/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSServiceType.cs index 23c73b344a..07ccd22a36 100644 --- a/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSServiceType.cs +++ b/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSServiceType.cs @@ -7,9 +7,9 @@ namespace OpenTelemetry.Instrumentation.AWS.Implementation; internal class AWSServiceType { - internal const string DynamoDbService = "DynamoDBv2"; + internal const string DynamoDbService = "DynamoDB"; internal const string SQSService = "SQS"; - internal const string SNSService = "SimpleNotificationService"; // SNS + internal const string SNSService = "SNS"; internal static bool IsDynamoDbService(string service) => DynamoDbService.Equals(service, StringComparison.OrdinalIgnoreCase); diff --git a/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSTracingPipelineCustomizer.cs b/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSTracingPipelineCustomizer.cs index d91bd8caf7..ac03e10452 100644 --- a/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSTracingPipelineCustomizer.cs +++ b/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSTracingPipelineCustomizer.cs @@ -31,6 +31,6 @@ public void Customize(Type serviceClientType, RuntimePipeline pipeline) return; } - pipeline.AddHandlerBefore(new AWSTracingPipelineHandler(this.options)); + pipeline.AddHandlerBefore(new AWSTracingPipelineHandler(this.options)); } } diff --git a/src/OpenTelemetry.Instrumentation.AWS/Implementation/SnsRequestContextHelper.cs b/src/OpenTelemetry.Instrumentation.AWS/Implementation/SnsRequestContextHelper.cs index 9a5eba3b8f..163e3b03af 100644 --- a/src/OpenTelemetry.Instrumentation.AWS/Implementation/SnsRequestContextHelper.cs +++ b/src/OpenTelemetry.Instrumentation.AWS/Implementation/SnsRequestContextHelper.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; using Amazon.Runtime; -using Amazon.Runtime.Internal; using Amazon.SimpleNotificationService.Model; namespace OpenTelemetry.Instrumentation.AWS.Implementation; @@ -18,9 +17,8 @@ internal class SnsRequestContextHelper internal static void AddAttributes(IRequestContext context, IReadOnlyDictionary attributes) { - var parameters = context.Request?.ParameterCollection; var originalRequest = context.OriginalRequest as PublishRequest; - if (originalRequest?.MessageAttributes == null || parameters == null) + if (originalRequest?.MessageAttributes == null) { return; } @@ -38,23 +36,9 @@ internal static void AddAttributes(IRequestContext context, IReadOnlyDictionary< return; } - int nextAttributeIndex = attributesCount + 1; foreach (var param in attributes) { - AddAttribute(parameters, originalRequest, param.Key, param.Value, nextAttributeIndex); - nextAttributeIndex++; + originalRequest.MessageAttributes[param.Key] = new MessageAttributeValue { DataType = "String", StringValue = param.Value }; } } - - private static void AddAttribute(ParameterCollection parameters, PublishRequest originalRequest, string name, string value, int attributeIndex) - { - var prefix = "MessageAttributes.entry." + attributeIndex; - parameters.Add(prefix + ".Name", name); - parameters.Add(prefix + ".Value.DataType", "String"); - parameters.Add(prefix + ".Value.StringValue", value); - - // Add injected attributes to the original request as well. - // This dictionary must be in sync with parameters collection to pass through the MD5 hash matching check. - originalRequest.MessageAttributes.Add(name, new MessageAttributeValue { DataType = "String", StringValue = value }); - } } diff --git a/src/OpenTelemetry.Instrumentation.AWS/Implementation/SqsRequestContextHelper.cs b/src/OpenTelemetry.Instrumentation.AWS/Implementation/SqsRequestContextHelper.cs index 33feb38172..534a120e90 100644 --- a/src/OpenTelemetry.Instrumentation.AWS/Implementation/SqsRequestContextHelper.cs +++ b/src/OpenTelemetry.Instrumentation.AWS/Implementation/SqsRequestContextHelper.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; using Amazon.Runtime; -using Amazon.Runtime.Internal; using Amazon.SQS.Model; namespace OpenTelemetry.Instrumentation.AWS.Implementation; @@ -18,9 +17,8 @@ internal class SqsRequestContextHelper internal static void AddAttributes(IRequestContext context, IReadOnlyDictionary attributes) { - var parameters = context.Request?.ParameterCollection; var originalRequest = context.OriginalRequest as SendMessageRequest; - if (originalRequest?.MessageAttributes == null || parameters == null) + if (originalRequest?.MessageAttributes == null) { return; } @@ -38,23 +36,9 @@ internal static void AddAttributes(IRequestContext context, IReadOnlyDictionary< return; } - int nextAttributeIndex = attributesCount + 1; foreach (var param in attributes) { - AddAttribute(parameters, originalRequest, param.Key, param.Value, nextAttributeIndex); - nextAttributeIndex++; + originalRequest.MessageAttributes[param.Key] = new MessageAttributeValue { DataType = "String", StringValue = param.Value }; } } - - private static void AddAttribute(ParameterCollection parameters, SendMessageRequest originalRequest, string name, string value, int attributeIndex) - { - var prefix = "MessageAttribute." + attributeIndex; - parameters.Add(prefix + ".Name", name); - parameters.Add(prefix + ".Value.DataType", "String"); - parameters.Add(prefix + ".Value.StringValue", value); - - // Add injected attributes to the original request as well. - // This dictionary must be in sync with parameters collection to pass through the MD5 hash matching check. - originalRequest.MessageAttributes.Add(name, new MessageAttributeValue { DataType = "String", StringValue = value }); - } } diff --git a/test/OpenTelemetry.Instrumentation.AWS.Tests/Implementation/RequestContextHelperTests.cs b/test/OpenTelemetry.Instrumentation.AWS.Tests/Implementation/RequestContextHelperTests.cs index 47466b52a5..6f8336633b 100644 --- a/test/OpenTelemetry.Instrumentation.AWS.Tests/Implementation/RequestContextHelperTests.cs +++ b/test/OpenTelemetry.Instrumentation.AWS.Tests/Implementation/RequestContextHelperTests.cs @@ -4,12 +4,13 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using Amazon.Runtime; using Amazon.Runtime.Internal; using OpenTelemetry.Context.Propagation; using OpenTelemetry.Instrumentation.AWS.Implementation; using OpenTelemetry.Trace; using Xunit; +using SNS = Amazon.SimpleNotificationService.Model; +using SQS = Amazon.SQS.Model; namespace OpenTelemetry.Instrumentation.AWS.Tests.Implementation; @@ -30,7 +31,7 @@ public RequestContextHelperTests() [InlineData(AWSServiceType.SNSService)] public void AddAttributes_ParametersCollectionSizeReachesLimit_TraceDataNotInjected(string serviceType) { - AmazonWebServiceRequest originalRequest = TestsHelper.CreateOriginalRequest(serviceType, 10); + var originalRequest = TestsHelper.CreateOriginalRequest(serviceType, 10); var parameters = new ParameterCollection(); parameters.AddStringParameters(serviceType, originalRequest); @@ -44,82 +45,82 @@ public void AddAttributes_ParametersCollectionSizeReachesLimit_TraceDataNotInjec Assert.Equal(30, parameters.Count); } - [Theory] - [InlineData(AWSServiceType.SQSService)] - [InlineData(AWSServiceType.SNSService)] - public void AddAttributes_ParametersCollection_TraceDataInjected(string serviceType) + [Fact] + public void SQS_AddAttributes_MessageAttributes_TraceDataInjected() { - var expectedParameters = new List>() + var expectedParameters = new List> { new("traceparent", $"00-{TraceId}-{ParentId}-00"), new("tracestate", "trace-state"), }; - AmazonWebServiceRequest originalRequest = TestsHelper.CreateOriginalRequest(serviceType, 0); - var parameters = new ParameterCollection(); + var originalRequest = new SQS.SendMessageRequest(); - var request = new TestRequest(parameters); - var context = new TestRequestContext(originalRequest, request); + var context = new TestRequestContext(originalRequest, new TestRequest()); - var addAttributes = TestsHelper.CreateAddAttributesAction(serviceType, context); - addAttributes?.Invoke(context, AWSMessagingUtils.InjectIntoDictionary(CreatePropagationContext())); + SqsRequestContextHelper.AddAttributes(context, AWSMessagingUtils.InjectIntoDictionary(CreatePropagationContext())); - TestsHelper.AssertStringParameters(serviceType, expectedParameters, parameters); + TestsHelper.AssertMessageParameters(expectedParameters, originalRequest); } - [Theory] - [InlineData(AWSServiceType.SQSService)] - [InlineData(AWSServiceType.SNSService)] - public void AddAttributes_ParametersCollectionWithCustomParameter_TraceDataInjected(string serviceType) + [Fact] + public void SNS_AddAttributes_MessageAttributes_TraceDataInjected() { - var expectedParameters = new List>() + var expectedParameters = new List> { - new("name1", "value1"), new("traceparent", $"00-{TraceId}-{ParentId}-00"), new("tracestate", "trace-state"), }; - AmazonWebServiceRequest originalRequest = TestsHelper.CreateOriginalRequest(serviceType, 1); - var parameters = new ParameterCollection(); - parameters.AddStringParameters(serviceType, originalRequest); - - var request = new TestRequest(parameters); + var originalRequest = new SNS.PublishRequest(); - var context = new TestRequestContext(originalRequest, request); + var context = new TestRequestContext(originalRequest, new TestRequest()); - var addAttributes = TestsHelper.CreateAddAttributesAction(serviceType, context); - addAttributes?.Invoke(context, AWSMessagingUtils.InjectIntoDictionary(CreatePropagationContext())); + SnsRequestContextHelper.AddAttributes(context, AWSMessagingUtils.InjectIntoDictionary(CreatePropagationContext())); - TestsHelper.AssertStringParameters(serviceType, expectedParameters, parameters); + TestsHelper.AssertMessageParameters(expectedParameters, originalRequest); } - [Theory] - [InlineData(AWSServiceType.SQSService)] - [InlineData(AWSServiceType.SNSService)] - public void AddAttributes_ParametersCollectionWithTraceParent_TraceStateNotInjected(string serviceType) + [Fact] + public void SQS_AddAttributes_MessageAttributesWithTraceParent_TraceStateNotInjected() { // This test just checks the common implementation logic: // if at least one attribute is already present the whole injection is skipped. // We just use default trace propagator as an example which injects only traceparent and tracestate. - var expectedParameters = new List>() + var expectedParameters = new List> { new("traceparent", $"00-{TraceId}-{ParentId}-00"), }; - AmazonWebServiceRequest originalRequest = TestsHelper.CreateOriginalRequest(serviceType, 0); - originalRequest.AddAttribute("traceparent", $"00-{TraceId}-{ParentId}-00"); + var originalRequest = new SQS.SendMessageRequest(); - var parameters = new ParameterCollection(); - parameters.AddStringParameters(serviceType, originalRequest); + var context = new TestRequestContext(originalRequest, new TestRequest()); - var request = new TestRequest(parameters); - var context = new TestRequestContext(originalRequest, request); + SqsRequestContextHelper.AddAttributes(context, AWSMessagingUtils.InjectIntoDictionary(CreatePropagationContext())); - var addAttributes = TestsHelper.CreateAddAttributesAction(serviceType, context); - addAttributes?.Invoke(context, AWSMessagingUtils.InjectIntoDictionary(CreatePropagationContext())); + TestsHelper.AssertMessageParameters(expectedParameters, originalRequest); + } + + [Fact] + public void SNS_AddAttributes_MessageAttributesWithTraceParent_TraceStateNotInjected() + { + // This test just checks the common implementation logic: + // if at least one attribute is already present the whole injection is skipped. + // We just use default trace propagator as an example which injects only traceparent and tracestate. + + var expectedParameters = new List> + { + new("traceparent", $"00-{TraceId}-{ParentId}-00"), + }; + + var originalRequest = new SNS.PublishRequest(); + + var context = new TestRequestContext(originalRequest, new TestRequest()); + + SnsRequestContextHelper.AddAttributes(context, AWSMessagingUtils.InjectIntoDictionary(CreatePropagationContext())); - TestsHelper.AssertStringParameters(serviceType, expectedParameters, parameters); + TestsHelper.AssertMessageParameters(expectedParameters, originalRequest); } private static PropagationContext CreatePropagationContext() diff --git a/test/OpenTelemetry.Instrumentation.AWS.Tests/Implementation/TestsHelper.cs b/test/OpenTelemetry.Instrumentation.AWS.Tests/Implementation/TestsHelper.cs index 3248b3c7d5..b6fd78aab0 100644 --- a/test/OpenTelemetry.Instrumentation.AWS.Tests/Implementation/TestsHelper.cs +++ b/test/OpenTelemetry.Instrumentation.AWS.Tests/Implementation/TestsHelper.cs @@ -14,6 +14,12 @@ namespace OpenTelemetry.Instrumentation.AWS.Tests.Implementation; internal static class TestsHelper { + /// + /// Returns either or + /// depending on . + /// + /// This is meant to mimic thee logic in . + /// internal static Action>? CreateAddAttributesAction(string serviceType, IRequestContext context) { return serviceType switch @@ -96,23 +102,23 @@ internal static void AddStringParameters(this ParameterCollection parameters, st } } - internal static void AssertStringParameters(string serviceType, List> expectedParameters, ParameterCollection parameters) + internal static void AssertMessageParameters(List> expectedParameters, SQS.SendMessageRequest request) { - Assert.Equal(expectedParameters.Count * 3, parameters.Count); - - for (int i = 0; i < expectedParameters.Count; i++) + foreach (var kvp in expectedParameters) { - var prefix = $"{GetNamePrefix(serviceType)}.{i + 1}"; - static string? Value(ParameterValue p) => (p as StringParameterValue)?.Value; + Assert.True(request.MessageAttributes.ContainsKey(kvp.Key)); - Assert.True(parameters.ContainsKey($"{prefix}.Name")); - Assert.Equal(expectedParameters[i].Key, Value(parameters[$"{prefix}.Name"])); + Assert.Equal(kvp.Value, request.MessageAttributes[kvp.Key].StringValue); + } + } - Assert.True(parameters.ContainsKey($"{prefix}.Value.DataType")); - Assert.Equal("String", Value(parameters[$"{prefix}.Value.DataType"])); + internal static void AssertMessageParameters(List> expectedParameters, SNS.PublishRequest request) + { + foreach (var kvp in expectedParameters) + { + Assert.True(request.MessageAttributes.ContainsKey(kvp.Key)); - Assert.True(parameters.ContainsKey($"{prefix}.Value.StringValue")); - Assert.Equal(expectedParameters[i].Value, Value(parameters[$"{prefix}.Value.StringValue"])); + Assert.Equal(kvp.Value, request.MessageAttributes[kvp.Key].StringValue); } } diff --git a/test/OpenTelemetry.Instrumentation.AWS.Tests/TestAWSClientInstrumentation.cs b/test/OpenTelemetry.Instrumentation.AWS.Tests/TestAWSClientInstrumentation.cs index c4f81f779e..81340c5764 100644 --- a/test/OpenTelemetry.Instrumentation.AWS.Tests/TestAWSClientInstrumentation.cs +++ b/test/OpenTelemetry.Instrumentation.AWS.Tests/TestAWSClientInstrumentation.cs @@ -174,6 +174,7 @@ public void TestSQSSendMessageSuccessful() var send_msg_req = new SendMessageRequest(); send_msg_req.QueueUrl = "https://sqs.us-east-1.amazonaws.com/123456789/MyTestQueue"; send_msg_req.MessageBody = "Hello from OT"; + send_msg_req.MessageAttributes.Add("Custom", new MessageAttributeValue { StringValue = "Value", DataType = "String" }); #if NETFRAMEWORK sqs.SendMessage(send_msg_req); #else @@ -198,8 +199,8 @@ private void ValidateAWSActivity(Activity aws_activity, Activity parent) private void ValidateDynamoActivityTags(Activity ddb_activity) { - Assert.Equal("DynamoDBv2.Scan", ddb_activity.DisplayName); - Assert.Equal("DynamoDBv2", Utils.GetTagValue(ddb_activity, "aws.service")); + Assert.Equal("DynamoDB.Scan", ddb_activity.DisplayName); + Assert.Equal("DynamoDB", Utils.GetTagValue(ddb_activity, "aws.service")); Assert.Equal("Scan", Utils.GetTagValue(ddb_activity, "aws.operation")); Assert.Equal("us-east-1", Utils.GetTagValue(ddb_activity, "aws.region")); Assert.Equal("SampleProduct", Utils.GetTagValue(ddb_activity, "aws.table_name")); diff --git a/test/OpenTelemetry.Instrumentation.AWS.Tests/TestRequest.cs b/test/OpenTelemetry.Instrumentation.AWS.Tests/TestRequest.cs index 2aa3d0a634..987c50cdf9 100644 --- a/test/OpenTelemetry.Instrumentation.AWS.Tests/TestRequest.cs +++ b/test/OpenTelemetry.Instrumentation.AWS.Tests/TestRequest.cs @@ -13,9 +13,13 @@ namespace OpenTelemetry.Instrumentation.AWS.Tests; -internal class TestRequest(ParameterCollection parameters) : IRequest +internal class TestRequest : IRequest { - private readonly ParameterCollection parameters = parameters; + private readonly ParameterCollection parameters; + public TestRequest(ParameterCollection? parameters = null) + { + this.parameters = parameters ?? new ParameterCollection(); + } public string RequestName => throw new NotImplementedException(); From 867ad3313ba259525a0fdf9aa5d4ea03b4fdd9ce Mon Sep 17 00:00:00 2001 From: philip pittle Date: Thu, 25 Jan 2024 22:43:10 -0800 Subject: [PATCH 3/4] [Instrumentation.AWS] add additioanl aws pipeline handler that activates after RequestContext.Request has been built so that the Propagator can inject request headers. --- .../AWSPropagatorPipelineHandler.cs | 68 +++++++++++++++++++ .../AWSTracingPipelineCustomizer.cs | 15 +++- .../AWSTracingPipelineHandler.cs | 39 ++++++----- 3 files changed, 101 insertions(+), 21 deletions(-) create mode 100644 src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSPropagatorPipelineHandler.cs diff --git a/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSPropagatorPipelineHandler.cs b/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSPropagatorPipelineHandler.cs new file mode 100644 index 0000000000..3f6ade4fd1 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSPropagatorPipelineHandler.cs @@ -0,0 +1,68 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Amazon.Runtime; +using Amazon.Runtime.Internal; +using OpenTelemetry.Context.Propagation; +using OpenTelemetry.Extensions.AWS.Trace; + +namespace OpenTelemetry.Instrumentation.AWS.Implementation; + +/// +/// Uses to inject the current Activity Context and +/// Baggage into the outgoing AWS SDK Request. +/// +/// Must execute after the AWS SDK has marshalled (ie serialized) +/// the outgoing request object so that it can work with the 's +/// . +/// +internal class AWSPropagatorPipelineHandler : PipelineHandler +{ + private static readonly AWSXRayPropagator AwsPropagator = new(); + + private static readonly Action, string, string> Setter = (carrier, name, value) => + { + carrier[name] = value; + }; + + /// + /// Rely on the the for retrieving the current + /// context. + /// + private readonly AWSTracingPipelineHandler tracingPipelineHandler; + + public AWSPropagatorPipelineHandler(AWSTracingPipelineHandler tracingPipelineHandler) + { + this.tracingPipelineHandler = tracingPipelineHandler; + } + + public override void InvokeSync(IExecutionContext executionContext) + { + this.ProcessBeginRequest(executionContext); + + base.InvokeSync(executionContext); + } + + public override async Task InvokeAsync(IExecutionContext executionContext) + { + this.ProcessBeginRequest(executionContext); + + return await base.InvokeAsync(executionContext).ConfigureAwait(false); + } + + private void ProcessBeginRequest(IExecutionContext executionContext) + { + if (this.tracingPipelineHandler.Activity == null) + { + return; + } + + AwsPropagator.Inject( + new PropagationContext(this.tracingPipelineHandler.Activity.Context, Baggage.Current), + executionContext.RequestContext.Request.Headers, + Setter); + } +} diff --git a/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSTracingPipelineCustomizer.cs b/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSTracingPipelineCustomizer.cs index ac03e10452..2a9538bb6c 100644 --- a/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSTracingPipelineCustomizer.cs +++ b/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSTracingPipelineCustomizer.cs @@ -7,6 +7,10 @@ namespace OpenTelemetry.Instrumentation.AWS.Implementation; +/// +/// Wires and +/// into the AWS so they can inject trace headers and wrap sdk calls in spans. +/// internal class AWSTracingPipelineCustomizer : IRuntimePipelineCustomizer { private readonly AWSClientInstrumentationOptions options; @@ -31,6 +35,15 @@ public void Customize(Type serviceClientType, RuntimePipeline pipeline) return; } - pipeline.AddHandlerBefore(new AWSTracingPipelineHandler(this.options)); + var tracingPipelineHandler = new AWSTracingPipelineHandler(this.options); + var propagatingPipelineHandler = new AWSPropagatorPipelineHandler(tracingPipelineHandler); + + // AWSTracingPipelineHandler must execute early in the AWS SDK pipeline + // in order to manipulate outgoing requests objects before they are marshalled (ie serialized). + pipeline.AddHandlerBefore(tracingPipelineHandler); + + // AWSPropagatorPipelineHandler executes after the AWS SDK has marshalled (ie serialized) + // the outgoing request object so that it can work with the request's Headers + pipeline.AddHandlerBefore(propagatingPipelineHandler); } } diff --git a/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSTracingPipelineHandler.cs b/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSTracingPipelineHandler.cs index 2383c9f8ba..fe4dfc3636 100644 --- a/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSTracingPipelineHandler.cs +++ b/src/OpenTelemetry.Instrumentation.AWS/Implementation/AWSTracingPipelineHandler.cs @@ -2,28 +2,27 @@ // SPDX-License-Identifier: Apache-2.0 using System; -using System.Collections.Generic; using System.Diagnostics; using System.Threading.Tasks; using Amazon.Runtime; using Amazon.Runtime.Internal; using Amazon.Util; using OpenTelemetry.Context.Propagation; -using OpenTelemetry.Extensions.AWS.Trace; using OpenTelemetry.Trace; namespace OpenTelemetry.Instrumentation.AWS.Implementation; +/// +/// Wraps the outgoing AWS SDK Request in a Span and adds additional AWS specific Tags. +/// Depending on the target AWS Service, additional request specific information may be injected as well. +/// +/// This must execute early in the AWS SDK pipeline +/// in order to manipulate outgoing requests objects before they are marshalled (ie serialized). +/// internal sealed class AWSTracingPipelineHandler : PipelineHandler { internal const string ActivitySourceName = "Amazon.AWS.AWSClientInstrumentation"; - private static readonly AWSXRayPropagator AwsPropagator = new(); - private static readonly Action, string, string> Setter = (carrier, name, value) => - { - carrier[name] = value; - }; - private static readonly ActivitySource AWSSDKActivitySource = new(ActivitySourceName); private readonly AWSClientInstrumentationOptions options; @@ -33,27 +32,29 @@ public AWSTracingPipelineHandler(AWSClientInstrumentationOptions options) this.options = options; } + public Activity? Activity { get; private set; } + public override void InvokeSync(IExecutionContext executionContext) { - var activity = this.ProcessBeginRequest(executionContext); + this.Activity = this.ProcessBeginRequest(executionContext); try { base.InvokeSync(executionContext); } catch (Exception ex) { - if (activity != null) + if (this.Activity != null) { - ProcessException(activity, ex); + ProcessException(this.Activity, ex); } throw; } finally { - if (activity != null) + if (this.Activity != null) { - ProcessEndRequest(executionContext, activity); + ProcessEndRequest(executionContext, this.Activity); } } } @@ -62,25 +63,25 @@ public override async Task InvokeAsync(IExecutionContext executionContext) { T? ret = null; - var activity = this.ProcessBeginRequest(executionContext); + this.Activity = this.ProcessBeginRequest(executionContext); try { ret = await base.InvokeAsync(executionContext).ConfigureAwait(false); } catch (Exception ex) { - if (activity != null) + if (this.Activity != null) { - ProcessException(activity, ex); + ProcessException(this.Activity, ex); } throw; } finally { - if (activity != null) + if (this.Activity != null) { - ProcessEndRequest(executionContext, activity); + ProcessEndRequest(executionContext, this.Activity); } } @@ -241,8 +242,6 @@ private static string FetchRequestId(IRequestContext requestContext, IResponseCo AddRequestSpecificInformation(activity, requestContext, service); } - AwsPropagator.Inject(new PropagationContext(activity.Context, Baggage.Current), requestContext.Request.Headers, Setter); - return activity; } } From 1ed3a85e0210db0f67fd8516a76ba886b485a12d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Thu, 22 Feb 2024 06:30:49 +0100 Subject: [PATCH 4/4] fix dotnet-format --- test/OpenTelemetry.Instrumentation.AWS.Tests/TestRequest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/OpenTelemetry.Instrumentation.AWS.Tests/TestRequest.cs b/test/OpenTelemetry.Instrumentation.AWS.Tests/TestRequest.cs index 987c50cdf9..a177693858 100644 --- a/test/OpenTelemetry.Instrumentation.AWS.Tests/TestRequest.cs +++ b/test/OpenTelemetry.Instrumentation.AWS.Tests/TestRequest.cs @@ -16,6 +16,7 @@ namespace OpenTelemetry.Instrumentation.AWS.Tests; internal class TestRequest : IRequest { private readonly ParameterCollection parameters; + public TestRequest(ParameterCollection? parameters = null) { this.parameters = parameters ?? new ParameterCollection();