From 5f13f6ec7985554a97cd903e7e89f8c64d696510 Mon Sep 17 00:00:00 2001 From: Henrique <999396+hjgraca@users.noreply.github.com> Date: Fri, 4 Oct 2024 09:03:10 +0100 Subject: [PATCH 1/3] revert removing IMethodAspectHandler. This is needed for async method parsing because aspect injector does not support it by default --- ...acingAspect.cs => TracingAspectHandler.cs} | 251 +++++++++--------- .../TracingAttribute.cs | 21 +- .../Handlers/FullExampleHandler.cs | 6 +- .../Handlers/HandlerTests.cs | 5 +- .../TracingAttributeTest.cs | 6 +- 5 files changed, 156 insertions(+), 133 deletions(-) rename libraries/src/AWS.Lambda.Powertools.Tracing/Internal/{TracingAspect.cs => TracingAspectHandler.cs} (57%) diff --git a/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/TracingAspect.cs b/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/TracingAspectHandler.cs similarity index 57% rename from libraries/src/AWS.Lambda.Powertools.Tracing/Internal/TracingAspect.cs rename to libraries/src/AWS.Lambda.Powertools.Tracing/Internal/TracingAspectHandler.cs index 46062d0d..7e80ab86 100644 --- a/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/TracingAspect.cs +++ b/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/TracingAspectHandler.cs @@ -1,12 +1,12 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * + * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at - * + * * http://aws.amazon.com/apache2.0 - * + * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing @@ -14,20 +14,18 @@ */ using System; -using System.Linq; using System.Runtime.ExceptionServices; using System.Text; -using AspectInjector.Broker; using AWS.Lambda.Powertools.Common; namespace AWS.Lambda.Powertools.Tracing.Internal; /// -/// This aspect will automatically trace all function handlers. -/// Scope.Global is singleton +/// Class TracingAspectHandler. +/// Implements the /// -[Aspect(Scope.Global)] -public class TracingAspect +/// +internal class TracingAspectHandler : IMethodAspectHandler { /// /// The Powertools for AWS Lambda (.NET) configurations @@ -48,130 +46,120 @@ public class TracingAspect /// If true, capture annotations /// private static bool _captureAnnotations = true; - + /// /// If true, annotations have been captured /// private bool _isAnnotationsCaptured; - + /// /// Tracing namespace /// private string _namespace; - + /// /// The capture mode /// private TracingCaptureMode _captureMode; + + /// + /// The segment name + /// + private readonly string _segmentName; /// - /// Initializes a new instance + /// Initializes a new instance of the class. /// - public TracingAspect() + /// Name of the segment. + /// The namespace. + /// The capture mode. + /// The Powertools for AWS Lambda (.NET) configurations. + /// The X-Ray recorder. + internal TracingAspectHandler + ( + string segmentName, + string nameSpace, + TracingCaptureMode captureMode, + IPowertoolsConfigurations powertoolsConfigurations, + IXRayRecorder xRayRecorder + ) { - _xRayRecorder = XRayRecorder.Instance; - _powertoolsConfigurations = PowertoolsConfigurations.Instance; + _segmentName = segmentName; + _namespace = nameSpace; + _captureMode = captureMode; + _powertoolsConfigurations = powertoolsConfigurations; + _xRayRecorder = xRayRecorder; } /// - /// the code is executed instead of the target method. - /// The call to original method is wrapped around the following code - /// the original code is called with var result = target(args); + /// Handles the event. /// - /// - /// - /// - /// - /// - [Advice(Kind.Around)] - public object Around( - [Argument(Source.Name)] string name, - [Argument(Source.Arguments)] object[] args, - [Argument(Source.Target)] Func target, - [Argument(Source.Triggers)] Attribute[] triggers) + /// + /// The instance containing the + /// event data. + /// + public void OnEntry(AspectEventArgs eventArgs) { - // Before running Function - - var trigger = triggers.OfType().First(); - try - { - if (TracingDisabled()) - return target(args); - - _namespace = trigger.Namespace; - - var segmentName = !string.IsNullOrWhiteSpace(trigger.SegmentName) ? trigger.SegmentName : $"## {name}"; - var nameSpace = GetNamespace(); - - _xRayRecorder.BeginSubsegment(segmentName); - _xRayRecorder.SetNamespace(nameSpace); - - if (_captureAnnotations) - { - _xRayRecorder.AddAnnotation("ColdStart", _isColdStart); - - _captureAnnotations = false; - _isAnnotationsCaptured = true; - - if (_powertoolsConfigurations.IsServiceDefined) - _xRayRecorder.AddAnnotation("Service", _powertoolsConfigurations.Service); - } - - _isColdStart = false; + if(TracingDisabled()) + return; - // return of the handler - var result = target(args); + var segmentName = !string.IsNullOrWhiteSpace(_segmentName) ? _segmentName : $"## {eventArgs.Name}"; + var nameSpace = GetNamespace(); - // must get capture after all subsegments run - _captureMode = trigger.CaptureMode; + _xRayRecorder.BeginSubsegment(segmentName); + _xRayRecorder.SetNamespace(nameSpace); - if (CaptureResponse()) - { - _xRayRecorder.AddMetadata - ( - nameSpace, - $"{name} response", - result - ); - } - - // after - return result; - } - catch (Exception e) + if (_captureAnnotations) { - _captureMode = trigger.CaptureMode; - HandleException(e, name); - throw; + _xRayRecorder.AddAnnotation("ColdStart", _isColdStart); + + _captureAnnotations = false; + _isAnnotationsCaptured = true; + + if (_powertoolsConfigurations.IsServiceDefined) + _xRayRecorder.AddAnnotation("Service", _powertoolsConfigurations.Service); } + + _isColdStart = false; } /// - /// the code is injected after the method ends. + /// Called when [success]. /// - [Advice(Kind.After)] - public void OnExit() + /// + /// The instance containing the + /// event data. + /// + /// The result. + public void OnSuccess(AspectEventArgs eventArgs, object result) { - if (TracingDisabled()) - return; - - if (_isAnnotationsCaptured) - _captureAnnotations = true; - - _xRayRecorder.EndSubsegment(); + if (CaptureResponse()) + { + var nameSpace = GetNamespace(); + + _xRayRecorder.AddMetadata + ( + nameSpace, + $"{eventArgs.Name} response", + result + ); + } } /// - /// Code that handles when exceptions occur in the client method + /// Called when [exception]. /// - /// - /// - private void HandleException(Exception exception, string name) + /// + /// The instance containing the + /// event data. + /// + /// The exception. + public void OnException(AspectEventArgs eventArgs, Exception exception) { if (CaptureError()) { var nameSpace = GetNamespace(); - + var sb = new StringBuilder(); sb.AppendLine($"Exception type: {exception.GetType()}"); sb.AppendLine($"Exception message: {exception.Message}"); @@ -185,48 +173,45 @@ private void HandleException(Exception exception, string name) sb.AppendLine($"Stack trace: {exception.InnerException.StackTrace}"); sb.AppendLine("---END Inner Exception"); } - + _xRayRecorder.AddMetadata ( nameSpace, - $"{name} error", + $"{eventArgs.Name} error", sb.ToString() ); } - // // The purpose of ExceptionDispatchInfo.Capture is to capture a potentially mutating exception's StackTrace at a point in time: - // // https://learn.microsoft.com/en-us/dotnet/standard/exceptions/best-practices-for-exceptions#capture-exceptions-to-rethrow-later + // The purpose of ExceptionDispatchInfo.Capture is to capture a potentially mutating exception's StackTrace at a point in time: + // https://learn.microsoft.com/en-us/dotnet/standard/exceptions/best-practices-for-exceptions#capture-exceptions-to-rethrow-later ExceptionDispatchInfo.Capture(exception).Throw(); } /// - /// Gets the namespace. + /// Handles the event. /// - /// System.String. - private string GetNamespace() + /// + /// The instance containing the + /// event data. + /// + public void OnExit(AspectEventArgs eventArgs) { - return !string.IsNullOrWhiteSpace(_namespace) ? _namespace : _powertoolsConfigurations.Service; + if(TracingDisabled()) + return; + + if (_isAnnotationsCaptured) + _captureAnnotations = true; + + _xRayRecorder.EndSubsegment(); } /// - /// Method that checks if tracing is disabled + /// Gets the namespace. /// - /// - private bool TracingDisabled() + /// System.String. + private string GetNamespace() { - if (_powertoolsConfigurations.TracingDisabled) - { - Console.WriteLine("Tracing has been disabled via env var POWERTOOLS_TRACE_DISABLED"); - return true; - } - - if (!_powertoolsConfigurations.IsLambdaEnvironment) - { - Console.WriteLine("Running outside Lambda environment; disabling Tracing"); - return true; - } - - return false; + return !string.IsNullOrWhiteSpace(_namespace) ? _namespace : _powertoolsConfigurations.Service; } /// @@ -235,6 +220,9 @@ private bool TracingDisabled() /// true if tracing should capture responses, false otherwise. private bool CaptureResponse() { + if(TracingDisabled()) + return false; + switch (_captureMode) { case TracingCaptureMode.EnvironmentVariable: @@ -255,9 +243,9 @@ private bool CaptureResponse() /// true if tracing should capture errors, false otherwise. private bool CaptureError() { - if (TracingDisabled()) + if(TracingDisabled()) return false; - + switch (_captureMode) { case TracingCaptureMode.EnvironmentVariable: @@ -271,7 +259,28 @@ private bool CaptureError() return false; } } + + /// + /// Tracing disabled. + /// + /// true if tracing is disabled, false otherwise. + private bool TracingDisabled() + { + if (_powertoolsConfigurations.TracingDisabled) + { + Console.WriteLine("Tracing has been disabled via env var POWERTOOLS_TRACE_DISABLED"); + return true; + } + + if (!_powertoolsConfigurations.IsLambdaEnvironment) + { + Console.WriteLine("Running outside Lambda environment; disabling Tracing"); + return true; + } + return false; + } + /// /// Resets static variables for test. /// @@ -280,4 +289,4 @@ internal static void ResetForTest() _isColdStart = true; _captureAnnotations = true; } -} \ No newline at end of file +} diff --git a/libraries/src/AWS.Lambda.Powertools.Tracing/TracingAttribute.cs b/libraries/src/AWS.Lambda.Powertools.Tracing/TracingAttribute.cs index 4ac0db2d..0d9d6727 100644 --- a/libraries/src/AWS.Lambda.Powertools.Tracing/TracingAttribute.cs +++ b/libraries/src/AWS.Lambda.Powertools.Tracing/TracingAttribute.cs @@ -13,8 +13,6 @@ * permissions and limitations under the License. */ -using System; -using AspectInjector.Broker; using AWS.Lambda.Powertools.Common; using AWS.Lambda.Powertools.Tracing.Internal; @@ -107,8 +105,7 @@ namespace AWS.Lambda.Powertools.Tracing; /// } /// /// -[Injection(typeof(TracingAspect))] -public class TracingAttribute : Attribute +public class TracingAttribute : MethodAspectAttribute { /// /// Set custom segment name for the operation. @@ -131,4 +128,20 @@ public class TracingAttribute : Attribute /// /// The capture mode. public TracingCaptureMode CaptureMode { get; set; } = TracingCaptureMode.EnvironmentVariable; + + /// + /// Creates the handler. + /// + /// IMethodAspectHandler. + protected override IMethodAspectHandler CreateHandler() + { + return new TracingAspectHandler + ( + SegmentName, + Namespace, + CaptureMode, + PowertoolsConfigurations.Instance, + XRayRecorder.Instance + ); + } } \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/Handlers/FullExampleHandler.cs b/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/Handlers/FullExampleHandler.cs index 5d1e78e5..68808be3 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/Handlers/FullExampleHandler.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/Handlers/FullExampleHandler.cs @@ -21,12 +21,12 @@ namespace AWS.Lambda.Powertools.Tracing.Tests.Handlers; public class FullExampleHandler { [Tracing(Namespace = "ns", CaptureMode = TracingCaptureMode.ResponseAndError)] - public Task Handle(string text, ILambdaContext context) + public async Task Handle(string text, ILambdaContext context) { Tracing.AddAnnotation("annotation", "value"); - BusinessLogic1().GetAwaiter().GetResult(); + await BusinessLogic1(); - return Task.FromResult(text.ToUpper()); + return await Task.FromResult(text.ToUpper()); } [Tracing(SegmentName = "First Call")] diff --git a/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/Handlers/HandlerTests.cs b/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/Handlers/HandlerTests.cs index 29f444cb..80a419b1 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/Handlers/HandlerTests.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/Handlers/HandlerTests.cs @@ -60,8 +60,9 @@ public async Task Full_Example() }; // Act + var facadeSegment = AWSXRayRecorder.Instance.TraceContext.GetEntity(); await handler.Handle("Hello World", context); - var handleSegment = AWSXRayRecorder.Instance.TraceContext.GetEntity(); + var handleSegment = facadeSegment.Subsegments[0]; // Assert Assert.True(handleSegment.IsAnnotationsAdded); @@ -106,6 +107,6 @@ public void Dispose() Environment.SetEnvironmentVariable("LAMBDA_TASK_ROOT", ""); Environment.SetEnvironmentVariable("POWERTOOLS_SERVICE_NAME", ""); Environment.SetEnvironmentVariable("POWERTOOLS_TRACE_DISABLED", ""); - TracingAspect.ResetForTest(); + TracingAspectHandler.ResetForTest(); } } \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/TracingAttributeTest.cs b/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/TracingAttributeTest.cs index 507ded2d..d9ff9a54 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/TracingAttributeTest.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/TracingAttributeTest.cs @@ -115,7 +115,7 @@ public void Dispose() Environment.SetEnvironmentVariable("POWERTOOLS_TRACER_CAPTURE_RESPONSE", ""); Environment.SetEnvironmentVariable("POWERTOOLS_TRACER_CAPTURE_ERROR", ""); Environment.SetEnvironmentVariable("POWERTOOLS_TRACE_DISABLED", ""); - TracingAspect.ResetForTest(); + TracingAspectHandler.ResetForTest(); } } @@ -171,7 +171,7 @@ private static void ClearEnvironment() Environment.SetEnvironmentVariable("POWERTOOLS_TRACER_CAPTURE_RESPONSE", ""); Environment.SetEnvironmentVariable("POWERTOOLS_TRACER_CAPTURE_ERROR", ""); Environment.SetEnvironmentVariable("POWERTOOLS_TRACE_DISABLED", ""); - TracingAspect.ResetForTest(); + TracingAspectHandler.ResetForTest(); } } @@ -757,7 +757,7 @@ public void Dispose() Environment.SetEnvironmentVariable("POWERTOOLS_TRACER_CAPTURE_RESPONSE", ""); Environment.SetEnvironmentVariable("POWERTOOLS_TRACER_CAPTURE_ERROR", ""); Environment.SetEnvironmentVariable("POWERTOOLS_TRACE_DISABLED", ""); - TracingAspect.ResetForTest(); + TracingAspectHandler.ResetForTest(); } } } \ No newline at end of file From 43b2aab2459be8166c37a288ce8cd46a457901d8 Mon Sep 17 00:00:00 2001 From: Henrique <999396+hjgraca@users.noreply.github.com> Date: Fri, 4 Oct 2024 09:17:54 +0100 Subject: [PATCH 2/3] fix sonar --- .../Internal/TracingAspectHandler.cs | 4 ++-- .../src/AWS.Lambda.Powertools.Tracing/TracingAttribute.cs | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/TracingAspectHandler.cs b/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/TracingAspectHandler.cs index 7e80ab86..c2863fce 100644 --- a/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/TracingAspectHandler.cs +++ b/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/TracingAspectHandler.cs @@ -55,12 +55,12 @@ internal class TracingAspectHandler : IMethodAspectHandler /// /// Tracing namespace /// - private string _namespace; + private readonly string _namespace; /// /// The capture mode /// - private TracingCaptureMode _captureMode; + private readonly TracingCaptureMode _captureMode; /// /// The segment name diff --git a/libraries/src/AWS.Lambda.Powertools.Tracing/TracingAttribute.cs b/libraries/src/AWS.Lambda.Powertools.Tracing/TracingAttribute.cs index 0d9d6727..c3a3a5c6 100644 --- a/libraries/src/AWS.Lambda.Powertools.Tracing/TracingAttribute.cs +++ b/libraries/src/AWS.Lambda.Powertools.Tracing/TracingAttribute.cs @@ -1,18 +1,19 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * + * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at - * + * * http://aws.amazon.com/apache2.0 - * + * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ +using System; using AWS.Lambda.Powertools.Common; using AWS.Lambda.Powertools.Tracing.Internal; @@ -105,6 +106,7 @@ namespace AWS.Lambda.Powertools.Tracing; /// } /// /// +[AttributeUsage(AttributeTargets.Method)] public class TracingAttribute : MethodAspectAttribute { /// From ba92787d46a376c054e0509c513f9d0a0b1af7de Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Fri, 4 Oct 2024 10:40:34 +0100 Subject: [PATCH 3/3] Update version for tracing --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 1a74e63c..ffcfa3f8 100644 --- a/version.json +++ b/version.json @@ -2,7 +2,7 @@ "Core": { "Logging": "1.6.1", "Metrics": "1.7.1", - "Tracing": "1.5.1" + "Tracing": "1.5.2" }, "Utilities": { "Parameters": "1.3.0",