Skip to content

Commit 871f18b

Browse files
authored
Merge pull request #632 from hjgraca/fix(tracing)-batch-handler-result-null-reference
chore: Fix Max depth serialization bug when using Batch RecordHanderResult and Tracing
2 parents e666521 + 5a4dd56 commit 871f18b

File tree

7 files changed

+174
-44
lines changed

7 files changed

+174
-44
lines changed

libraries/src/AWS.Lambda.Powertools.BatchProcessing/RecordHandlerResult.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class RecordHandlerResult
2323
/// <summary>
2424
/// Returns an empty <see cref="RecordHandlerResult"/> value.
2525
/// </summary>
26-
public static readonly RecordHandlerResult None = new();
26+
public static RecordHandlerResult None { get; } = null!;
2727

2828
/// <summary>
2929
/// Convenience method for the creation of a <see cref="RecordHandlerResult"/>.

libraries/src/AWS.Lambda.Powertools.Tracing/Internal/TracingAspect.cs

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
using System;
1717
using System.Linq;
18-
using System.Reflection;
1918
using System.Runtime.ExceptionServices;
2019
using System.Text;
2120
using AspectInjector.Broker;
@@ -39,7 +38,7 @@ public class TracingAspect
3938
/// X-Ray Recorder
4039
/// </summary>
4140
private readonly IXRayRecorder _xRayRecorder;
42-
41+
4342
/// <summary>
4443
/// If true, then is cold start
4544
/// </summary>
@@ -49,17 +48,17 @@ public class TracingAspect
4948
/// If true, capture annotations
5049
/// </summary>
5150
private static bool _captureAnnotations = true;
52-
51+
5352
/// <summary>
5453
/// If true, annotations have been captured
5554
/// </summary>
5655
private bool _isAnnotationsCaptured;
57-
56+
5857
/// <summary>
5958
/// Tracing namespace
6059
/// </summary>
6160
private string _namespace;
62-
61+
6362
/// <summary>
6463
/// The capture mode
6564
/// </summary>
@@ -73,18 +72,14 @@ public TracingAspect()
7372
_xRayRecorder = XRayRecorder.Instance;
7473
_powertoolsConfigurations = PowertoolsConfigurations.Instance;
7574
}
76-
75+
7776
/// <summary>
7877
/// the code is executed instead of the target method.
7978
/// The call to original method is wrapped around the following code
8079
/// the original code is called with var result = target(args);
8180
/// </summary>
82-
/// <param name="instance"></param>
8381
/// <param name="name"></param>
8482
/// <param name="args"></param>
85-
/// <param name="hostType"></param>
86-
/// <param name="method"></param>
87-
/// <param name="returnType"></param>
8883
/// <param name="target"></param>
8984
/// <param name="triggers"></param>
9085
/// <returns></returns>
@@ -97,38 +92,39 @@ public object Around(
9792
{
9893
// Before running Function
9994

95+
var trigger = triggers.OfType<TracingAttribute>().First();
10096
try
10197
{
102-
var trigger = triggers.OfType<TracingAttribute>().First();
103-
104-
if(TracingDisabled())
98+
if (TracingDisabled())
10599
return target(args);
106100

107101
_namespace = trigger.Namespace;
108-
_captureMode = trigger.CaptureMode;
109-
102+
110103
var segmentName = !string.IsNullOrWhiteSpace(trigger.SegmentName) ? trigger.SegmentName : $"## {name}";
111104
var nameSpace = GetNamespace();
112105

113106
_xRayRecorder.BeginSubsegment(segmentName);
114107
_xRayRecorder.SetNamespace(nameSpace);
115108

116-
if (_captureAnnotations)
109+
if (_captureAnnotations)
117110
{
118111
_xRayRecorder.AddAnnotation("ColdStart", _isColdStart);
119-
112+
120113
_captureAnnotations = false;
121114
_isAnnotationsCaptured = true;
122115

123116
if (_powertoolsConfigurations.IsServiceDefined)
124117
_xRayRecorder.AddAnnotation("Service", _powertoolsConfigurations.Service);
125118
}
126-
119+
127120
_isColdStart = false;
128-
121+
129122
// return of the handler
130123
var result = target(args);
131-
124+
125+
// must get capture after all subsegments run
126+
_captureMode = trigger.CaptureMode;
127+
132128
if (CaptureResponse())
133129
{
134130
_xRayRecorder.AddMetadata
@@ -138,12 +134,13 @@ public object Around(
138134
result
139135
);
140136
}
141-
137+
142138
// after
143139
return result;
144140
}
145141
catch (Exception e)
146142
{
143+
_captureMode = trigger.CaptureMode;
147144
HandleException(e, name);
148145
throw;
149146
}
@@ -153,16 +150,17 @@ public object Around(
153150
/// the code is injected after the method ends.
154151
/// </summary>
155152
[Advice(Kind.After)]
156-
public void OnExit() {
157-
if(TracingDisabled())
153+
public void OnExit()
154+
{
155+
if (TracingDisabled())
158156
return;
159157

160158
if (_isAnnotationsCaptured)
161159
_captureAnnotations = true;
162160

163161
_xRayRecorder.EndSubsegment();
164162
}
165-
163+
166164
/// <summary>
167165
/// Code that handles when exceptions occur in the client method
168166
/// </summary>
@@ -173,7 +171,7 @@ private void HandleException(Exception exception, string name)
173171
if (CaptureError())
174172
{
175173
var nameSpace = GetNamespace();
176-
174+
177175
var sb = new StringBuilder();
178176
sb.AppendLine($"Exception type: {exception.GetType()}");
179177
sb.AppendLine($"Exception message: {exception.Message}");
@@ -187,7 +185,7 @@ private void HandleException(Exception exception, string name)
187185
sb.AppendLine($"Stack trace: {exception.InnerException.StackTrace}");
188186
sb.AppendLine("---END Inner Exception");
189187
}
190-
188+
191189
_xRayRecorder.AddMetadata
192190
(
193191
nameSpace,
@@ -209,7 +207,7 @@ private string GetNamespace()
209207
{
210208
return !string.IsNullOrWhiteSpace(_namespace) ? _namespace : _powertoolsConfigurations.Service;
211209
}
212-
210+
213211
/// <summary>
214212
/// Method that checks if tracing is disabled
215213
/// </summary>
@@ -230,7 +228,7 @@ private bool TracingDisabled()
230228

231229
return false;
232230
}
233-
231+
234232
/// <summary>
235233
/// Captures the response.
236234
/// </summary>
@@ -250,16 +248,16 @@ private bool CaptureResponse()
250248
return false;
251249
}
252250
}
253-
251+
254252
/// <summary>
255253
/// Captures the error.
256254
/// </summary>
257255
/// <returns><c>true</c> if tracing should capture errors, <c>false</c> otherwise.</returns>
258256
private bool CaptureError()
259257
{
260-
if(TracingDisabled())
258+
if (TracingDisabled())
261259
return false;
262-
260+
263261
switch (_captureMode)
264262
{
265263
case TracingCaptureMode.EnvironmentVariable:
@@ -273,7 +271,7 @@ private bool CaptureError()
273271
return false;
274272
}
275273
}
276-
274+
277275
/// <summary>
278276
/// Resets static variables for test.
279277
/// </summary>

libraries/src/AWS.Lambda.Powertools.Tracing/Internal/XRayRecorder.cs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/*
22
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3-
*
3+
*
44
* Licensed under the Apache License, Version 2.0 (the "License").
55
* You may not use this file except in compliance with the License.
66
* A copy of the License is located at
7-
*
7+
*
88
* http://aws.amazon.com/apache2.0
9-
*
9+
*
1010
* or in the "license" file accompanying this file. This file is distributed
1111
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
1212
* express or implied. See the License for the specific language governing
@@ -41,7 +41,8 @@ internal class XRayRecorder : IXRayRecorder
4141
/// Gets the instance.
4242
/// </summary>
4343
/// <value>The instance.</value>
44-
public static IXRayRecorder Instance => _instance ??= new XRayRecorder(AWSXRayRecorder.Instance, PowertoolsConfigurations.Instance);
44+
public static IXRayRecorder Instance =>
45+
_instance ??= new XRayRecorder(AWSXRayRecorder.Instance, PowertoolsConfigurations.Instance);
4546

4647
public XRayRecorder(IAWSXRayRecorder awsxRayRecorder, IPowertoolsConfigurations powertoolsConfigurations)
4748
{
@@ -56,7 +57,7 @@ public XRayRecorder(IAWSXRayRecorder awsxRayRecorder, IPowertoolsConfigurations
5657
/// Checks whether current execution is in AWS Lambda.
5758
/// </summary>
5859
/// <returns>Returns true if current execution is in AWS Lambda.</returns>
59-
private static bool _isLambda;
60+
private static bool _isLambda;
6061

6162
/// <summary>
6263
/// Gets the emitter.
@@ -112,14 +113,30 @@ public void AddMetadata(string nameSpace, string key, object value)
112113
if (_isLambda)
113114
_awsxRayRecorder.AddMetadata(nameSpace, key, value);
114115
}
115-
116+
116117
/// <summary>
117118
/// Ends the subsegment.
118119
/// </summary>
119120
public void EndSubsegment()
120121
{
121-
if (_isLambda)
122+
if (!_isLambda) return;
123+
try
124+
{
125+
_awsxRayRecorder.EndSubsegment();
126+
}
127+
catch (Exception e)
128+
{
129+
// if it fails at this stage the data is lost
130+
// so lets create a new subsegment with the error
131+
132+
Console.WriteLine("Error in Tracing utility - see Exceptions tab in Cloudwatch Traces");
133+
134+
_awsxRayRecorder.TraceContext.ClearEntity();
135+
_awsxRayRecorder.BeginSubsegment("Error in Tracing utility - see Exceptions tab");
136+
_awsxRayRecorder.AddException(e);
137+
_awsxRayRecorder.MarkError();
122138
_awsxRayRecorder.EndSubsegment();
139+
}
123140
}
124141

125142
/// <summary>

libraries/src/Directory.Packages.props

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99
<PackageVersion Include="AWSSDK.AppConfig" Version="3.7.301" />
1010
<PackageVersion Include="AWSSDK.AppConfigData" Version="3.7.301.15" />
1111
<PackageVersion Include="AWSSDK.DynamoDBv2" Version="3.7.301.18" />
12-
<PackageVersion Include="AWSXRayRecorder.Handlers.AwsSdk" Version="2.12.0" />
12+
<PackageVersion Include="AWSXRayRecorder.Handlers.AwsSdk" Version="2.13.0" />
1313
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
1414
<PackageVersion Include="AWSSDK.SecretsManager" Version="3.7.302.29" />
1515
<PackageVersion Include="AWSSDK.SimpleSystemsManagement" Version="3.7.303.2" />
1616
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
17-
<PackageVersion Include="AWSSDK.XRay" Version="3.7.300.54" />
18-
<PackageVersion Include="AWSXRayRecorder.Core" Version="2.14.0" />
17+
<PackageVersion Include="AWSSDK.XRay" Version="3.7.400.8" />
18+
<PackageVersion Include="AWSXRayRecorder.Core" Version="2.15.0" />
1919
<PackageVersion Include="Amazon.Lambda.DynamoDBEvents" Version="3.1.0" />
2020
<PackageVersion Include="Amazon.Lambda.KinesisEvents" Version="2.2.0" />
2121
<PackageVersion Include="Amazon.Lambda.SQSEvents" Version="2.2.0" />

libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/Handlers/Handlers.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,24 @@ public string[] HandleWithCaptureModeDisabled(bool exception = false)
8383
throw new Exception("Failed");
8484
return new[] { "A", "B" };
8585
}
86+
87+
[Tracing(CaptureMode = TracingCaptureMode.ResponseAndError)]
88+
public string DecoratedHandlerCaptureResponse()
89+
{
90+
DecoratedMethodCaptureDisabled();
91+
return "Hello World";
92+
}
93+
94+
[Tracing(CaptureMode = TracingCaptureMode.Disabled)]
95+
public string DecoratedMethodCaptureDisabled()
96+
{
97+
DecoratedMethodCaptureEnabled();
98+
return "DecoratedMethod Disabled";
99+
}
100+
101+
[Tracing(CaptureMode = TracingCaptureMode.ResponseAndError)]
102+
private string DecoratedMethodCaptureEnabled()
103+
{
104+
return "DecoratedMethod Enabled";
105+
}
86106
}

libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/TracingAttributeTest.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,69 @@ public void OnSuccess_WhenTracerCaptureModeIsDisabled_DoesNotCaptureResponse()
429429
Assert.Single(segment.Subsegments);
430430
Assert.False(subSegment.IsMetadataAdded); // does not add metadata
431431
}
432+
433+
[Fact]
434+
public void OnSuccess_WhenTracerCaptureResponseEnvironmentVariableIsFalse_ButDecoratorCapturesResponse()
435+
{
436+
// Arrange
437+
Environment.SetEnvironmentVariable("LAMBDA_TASK_ROOT", "AWS");
438+
Environment.SetEnvironmentVariable("POWERTOOLS_SERVICE_NAME", "POWERTOOLS");
439+
Environment.SetEnvironmentVariable("POWERTOOLS_TRACER_CAPTURE_RESPONSE", "false");
440+
441+
// Act
442+
var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity();
443+
_handler.DecoratedHandlerCaptureResponse();
444+
var subSegment = segment.Subsegments[0];
445+
446+
// Assert
447+
Assert.True(segment.IsSubsegmentsAdded);
448+
Assert.Single(segment.Subsegments);
449+
Assert.True(subSegment.IsMetadataAdded);
450+
Assert.True(subSegment.Metadata.ContainsKey("POWERTOOLS"));
451+
452+
var metadata = subSegment.Metadata["POWERTOOLS"];
453+
Assert.Equal("DecoratedHandlerCaptureResponse response", metadata.Keys.Cast<string>().First());
454+
var handlerResponse = metadata.Values.Cast<string>().First();
455+
Assert.Equal("Hello World", handlerResponse);
456+
457+
var decoratedMethodSegmentDisabled = subSegment.Subsegments[0];
458+
Assert.False(decoratedMethodSegmentDisabled.IsMetadataAdded);
459+
Assert.Equal("## DecoratedMethodCaptureDisabled", decoratedMethodSegmentDisabled.Name);
460+
461+
var decoratedMethodSegmentEnabled = decoratedMethodSegmentDisabled.Subsegments[0];
462+
Assert.True(decoratedMethodSegmentEnabled.IsMetadataAdded);
463+
464+
var decoratedMethodSegmentEnabledMetadata = decoratedMethodSegmentEnabled.Metadata["POWERTOOLS"];
465+
var decoratedMethodSegmentEnabledResponse = decoratedMethodSegmentEnabledMetadata.Values.Cast<string>().First();
466+
Assert.Equal("DecoratedMethod Enabled", decoratedMethodSegmentEnabledResponse);
467+
Assert.Equal("## DecoratedMethodCaptureEnabled", decoratedMethodSegmentEnabled.Name);
468+
}
469+
470+
[Fact]
471+
public void OnSuccess_WhenTracerCaptureResponseEnvironmentVariableIsTrue_ButDecoratorCapturesResponseDisabled()
472+
{
473+
// Arrange
474+
Environment.SetEnvironmentVariable("LAMBDA_TASK_ROOT", "AWS");
475+
Environment.SetEnvironmentVariable("POWERTOOLS_SERVICE_NAME", "POWERTOOLS");
476+
Environment.SetEnvironmentVariable("POWERTOOLS_TRACER_CAPTURE_RESPONSE", "true");
477+
478+
// Act
479+
var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity();
480+
_handler.DecoratedMethodCaptureDisabled();
481+
var subSegment = segment.Subsegments[0];
482+
483+
// Assert
484+
Assert.True(segment.IsSubsegmentsAdded);
485+
Assert.Single(segment.Subsegments);
486+
Assert.False(subSegment.IsMetadataAdded);
487+
488+
var decoratedMethodSegmentEnabled = subSegment.Subsegments[0];
489+
var metadata = decoratedMethodSegmentEnabled.Metadata["POWERTOOLS"];
490+
Assert.True(decoratedMethodSegmentEnabled.IsMetadataAdded);
491+
var decoratedMethodSegmentEnabledResponse = metadata.Values.Cast<string>().First();
492+
Assert.Equal("DecoratedMethod Enabled", decoratedMethodSegmentEnabledResponse);
493+
Assert.Equal("## DecoratedMethodCaptureEnabled", decoratedMethodSegmentEnabled.Name);
494+
}
432495

433496
#endregion
434497

0 commit comments

Comments
 (0)