From 0475567035c36e71ad6fd26f99ccb98d826a11c9 Mon Sep 17 00:00:00 2001 From: Amir Khairalomoum Date: Sun, 27 Feb 2022 21:04:58 +0000 Subject: [PATCH 1/2] examples clean up & bug fix --- examples/Tracing/src/HelloWorld/Function.cs | 21 ++++--- .../test/HelloWorld.Test/FunctionTest.cs | 57 ++++++++++++------- .../HelloWorld.Test/HelloWorld.Tests.csproj | 2 + 3 files changed, 54 insertions(+), 26 deletions(-) diff --git a/examples/Tracing/src/HelloWorld/Function.cs b/examples/Tracing/src/HelloWorld/Function.cs index 342e3e29..4f5b2ea2 100644 --- a/examples/Tracing/src/HelloWorld/Function.cs +++ b/examples/Tracing/src/HelloWorld/Function.cs @@ -42,6 +42,15 @@ public Function() var config = new DynamoDBContextConfig { Conversion = DynamoDBEntryConversion.V2 }; _dynamoDbContext = new DynamoDBContext(new AmazonDynamoDBClient(), config); } + + /// + /// Test constructor + /// + public Function(IDynamoDBContext dynamoDbContext, HttpClient httpClient) + { + _httpClient = httpClient; + _dynamoDbContext = dynamoDbContext; + } /// /// Lambda Handler @@ -58,8 +67,7 @@ public async Task FunctionHandler(APIGatewayProxyReques Logger.LogInformation("Getting ip address from external service"); - - var location = await GetCallingIp(); + var location = await GetCallingIp().ConfigureAwait(false); var lookupRecord = new LookupRecord(lookupId: requestContextRequestId, greeting: "Hello AWS Lambda Powertools for .NET", ipAddress: location); @@ -124,18 +132,17 @@ public async Task FunctionHandler(APIGatewayProxyReques } /// - /// Saves the LookupRecord record in DynamoDB + /// Saves the lookup record in DynamoDB /// /// Instance of LookupRecord - /// LookupRecord + /// A Task that can be used to poll or wait for results, or both. [Tracing(SegmentName = "DynamoDB")] - private static Task SaveRecordInDynamo(LookupRecord lookupRecord) + private static async Task SaveRecordInDynamo(LookupRecord lookupRecord) { try { Logger.LogInformation($"Saving record with id {lookupRecord.LookupId}"); - _dynamoDbContext?.SaveAsync(lookupRecord).Wait(); - return Task.FromResult(lookupRecord); + await _dynamoDbContext?.SaveAsync(lookupRecord)!; } catch (AmazonDynamoDBException e) { diff --git a/examples/Tracing/test/HelloWorld.Test/FunctionTest.cs b/examples/Tracing/test/HelloWorld.Test/FunctionTest.cs index dabd5dfd..ea1dab4c 100644 --- a/examples/Tracing/test/HelloWorld.Test/FunctionTest.cs +++ b/examples/Tracing/test/HelloWorld.Test/FunctionTest.cs @@ -1,11 +1,16 @@ using System; using System.Collections.Generic; +using System.Net; using System.Net.Http; using System.Text.Json; +using System.Threading; using System.Threading.Tasks; +using Amazon.DynamoDBv2.DataModel; using Xunit; using Amazon.Lambda.APIGatewayEvents; using Amazon.Lambda.TestUtilities; +using Moq; +using Moq.Protected; using Xunit.Abstractions; namespace HelloWorld.Tests @@ -13,31 +18,45 @@ namespace HelloWorld.Tests public class FunctionTest { private readonly ITestOutputHelper _testOutputHelper; - private static readonly HttpClient Client = new HttpClient(); - + public FunctionTest(ITestOutputHelper testOutputHelper) { _testOutputHelper = testOutputHelper; } - private static async Task GetCallingIP() - { - Client.DefaultRequestHeaders.Accept.Clear(); - Client.DefaultRequestHeaders.Add("User-Agent", "AWS Lambda .Net Client"); - - var stringTask = Client.GetStringAsync("http://checkip.amazonaws.com/") - .ConfigureAwait(continueOnCapturedContext: false); - - var msg = await stringTask; - return msg.Replace("\n", ""); - } - [Fact] public async Task TestHelloWorldFunctionHandler() { var requestId = Guid.NewGuid().ToString("D"); - var request = new APIGatewayProxyRequest() - { RequestContext = new APIGatewayProxyRequest.ProxyRequestContext() { RequestId = requestId } }; + var accountId = Guid.NewGuid().ToString("D"); + var location = "192.158. 1.38"; + Environment.SetEnvironmentVariable("POWERTOOLS_SERVICE_NAME","powertools-dotnet-sample"); + + var dynamoDbContext = new Mock(); + var handlerMock = new Mock(); + handlerMock + .Protected() + .Setup>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny() + ) + .ReturnsAsync(new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent(location) + }) + .Verifiable(); + + var request = new APIGatewayProxyRequest + { + RequestContext = new APIGatewayProxyRequest.ProxyRequestContext + { + RequestId = requestId, + AccountId = accountId + } + }; + var context = new TestLambdaContext() { FunctionName = "PowertoolsLoggingSample-HelloWorldFunction-Gg8rhPwO7Wa1", @@ -45,8 +64,8 @@ public async Task TestHelloWorldFunctionHandler() MemoryLimitInMB = 215, AwsRequestId = Guid.NewGuid().ToString("D") }; - string location = GetCallingIP().Result; - Dictionary body = new Dictionary + + var body = new Dictionary { { "LookupId", requestId }, { "Greeting", "Hello AWS Lambda Powertools for .NET" }, @@ -60,7 +79,7 @@ public async Task TestHelloWorldFunctionHandler() Headers = new Dictionary { { "Content-Type", "application/json" } } }; - var function = new Function(); + var function = new Function(dynamoDbContext.Object, new HttpClient(handlerMock.Object)); var response = await function.FunctionHandler(request, context); _testOutputHelper.WriteLine("Lambda Response: \n" + response.Body); diff --git a/examples/Tracing/test/HelloWorld.Test/HelloWorld.Tests.csproj b/examples/Tracing/test/HelloWorld.Test/HelloWorld.Tests.csproj index 512a14d9..be3f49fd 100644 --- a/examples/Tracing/test/HelloWorld.Test/HelloWorld.Tests.csproj +++ b/examples/Tracing/test/HelloWorld.Test/HelloWorld.Tests.csproj @@ -6,7 +6,9 @@ + + From d1d58a6ac57e9b11458c9af14a0c685c7180dc9b Mon Sep 17 00:00:00 2001 From: Amir Khairalomoum Date: Sun, 27 Feb 2022 21:07:21 +0000 Subject: [PATCH 2/2] examples clean up & bug fix --- examples/Logging/src/HelloWorld/Function.cs | 23 ++++++-- .../test/HelloWorld.Test/FunctionTest.cs | 57 ++++++++++++------- .../HelloWorld.Test/HelloWorld.Tests.csproj | 2 + examples/Metrics/src/HelloWorld/Function.cs | 18 ++++-- .../test/HelloWorld.Test/FunctionTest.cs | 55 ++++++++++++------ .../HelloWorld.Test/HelloWorld.Tests.csproj | 2 + .../Internal/XRayRecorder.cs | 38 +++++++++---- .../AWS.Lambda.Powertools.Tracing/Tracing.cs | 13 +++-- 8 files changed, 144 insertions(+), 64 deletions(-) diff --git a/examples/Logging/src/HelloWorld/Function.cs b/examples/Logging/src/HelloWorld/Function.cs index 4ef91a25..79c83514 100644 --- a/examples/Logging/src/HelloWorld/Function.cs +++ b/examples/Logging/src/HelloWorld/Function.cs @@ -21,7 +21,10 @@ public class Function private static HttpClient? _httpClient; private static IDynamoDBContext? _dynamoDbContext; - public Function() + /// + /// Function constructor + /// + public Function() { _httpClient = new HttpClient(); @@ -36,6 +39,15 @@ public Function() var config = new DynamoDBContextConfig { Conversion = DynamoDBEntryConversion.V2 }; _dynamoDbContext = new DynamoDBContext(new AmazonDynamoDBClient(), config); } + + /// + /// Test constructor + /// + public Function(IDynamoDBContext dynamoDbContext, HttpClient httpClient) + { + _httpClient = httpClient; + _dynamoDbContext = dynamoDbContext; + } [Logging(LogEvent = true)] public async Task FunctionHandler(APIGatewayProxyRequest apigwProxyEvent, @@ -108,17 +120,16 @@ public async Task FunctionHandler(APIGatewayProxyReques } /// - /// Saves the loopup record in DynamoDB + /// Saves the lookup record in DynamoDB /// /// - /// - private static Task SaveRecordInDynamo(LookupRecord lookupRecord) + /// A Task that can be used to poll or wait for results, or both. + private static async Task SaveRecordInDynamo(LookupRecord lookupRecord) { try { Logger.LogInformation($"Saving record with id {lookupRecord.LookupId}"); - _dynamoDbContext?.SaveAsync(lookupRecord).Wait(); - return Task.FromResult(lookupRecord); + await _dynamoDbContext?.SaveAsync(lookupRecord)!; } catch (AmazonDynamoDBException e) { diff --git a/examples/Logging/test/HelloWorld.Test/FunctionTest.cs b/examples/Logging/test/HelloWorld.Test/FunctionTest.cs index dabd5dfd..c57b14e1 100644 --- a/examples/Logging/test/HelloWorld.Test/FunctionTest.cs +++ b/examples/Logging/test/HelloWorld.Test/FunctionTest.cs @@ -1,11 +1,16 @@ using System; using System.Collections.Generic; +using System.Net; using System.Net.Http; using System.Text.Json; +using System.Threading; using System.Threading.Tasks; +using Amazon.DynamoDBv2.DataModel; using Xunit; using Amazon.Lambda.APIGatewayEvents; using Amazon.Lambda.TestUtilities; +using Moq; +using Moq.Protected; using Xunit.Abstractions; namespace HelloWorld.Tests @@ -13,40 +18,54 @@ namespace HelloWorld.Tests public class FunctionTest { private readonly ITestOutputHelper _testOutputHelper; - private static readonly HttpClient Client = new HttpClient(); public FunctionTest(ITestOutputHelper testOutputHelper) { _testOutputHelper = testOutputHelper; } - private static async Task GetCallingIP() - { - Client.DefaultRequestHeaders.Accept.Clear(); - Client.DefaultRequestHeaders.Add("User-Agent", "AWS Lambda .Net Client"); - - var stringTask = Client.GetStringAsync("http://checkip.amazonaws.com/") - .ConfigureAwait(continueOnCapturedContext: false); - - var msg = await stringTask; - return msg.Replace("\n", ""); - } - [Fact] public async Task TestHelloWorldFunctionHandler() { + // Arrange var requestId = Guid.NewGuid().ToString("D"); - var request = new APIGatewayProxyRequest() - { RequestContext = new APIGatewayProxyRequest.ProxyRequestContext() { RequestId = requestId } }; - var context = new TestLambdaContext() + var accountId = Guid.NewGuid().ToString("D"); + var location = "192.158. 1.38"; + + var dynamoDbContext = new Mock(); + var handlerMock = new Mock(); + handlerMock + .Protected() + .Setup>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny() + ) + .ReturnsAsync(new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent(location) + }) + .Verifiable(); + + var request = new APIGatewayProxyRequest + { + RequestContext = new APIGatewayProxyRequest.ProxyRequestContext + { + RequestId = requestId, + AccountId = accountId + } + }; + + var context = new TestLambdaContext { FunctionName = "PowertoolsLoggingSample-HelloWorldFunction-Gg8rhPwO7Wa1", FunctionVersion = "1", MemoryLimitInMB = 215, AwsRequestId = Guid.NewGuid().ToString("D") }; - string location = GetCallingIP().Result; - Dictionary body = new Dictionary + + var body = new Dictionary { { "LookupId", requestId }, { "Greeting", "Hello AWS Lambda Powertools for .NET" }, @@ -60,7 +79,7 @@ public async Task TestHelloWorldFunctionHandler() Headers = new Dictionary { { "Content-Type", "application/json" } } }; - var function = new Function(); + var function = new Function(dynamoDbContext.Object, new HttpClient(handlerMock.Object)); var response = await function.FunctionHandler(request, context); _testOutputHelper.WriteLine("Lambda Response: \n" + response.Body); diff --git a/examples/Logging/test/HelloWorld.Test/HelloWorld.Tests.csproj b/examples/Logging/test/HelloWorld.Test/HelloWorld.Tests.csproj index 512a14d9..be3f49fd 100644 --- a/examples/Logging/test/HelloWorld.Test/HelloWorld.Tests.csproj +++ b/examples/Logging/test/HelloWorld.Test/HelloWorld.Tests.csproj @@ -6,7 +6,9 @@ + + diff --git a/examples/Metrics/src/HelloWorld/Function.cs b/examples/Metrics/src/HelloWorld/Function.cs index bb6b128b..ef288f58 100644 --- a/examples/Metrics/src/HelloWorld/Function.cs +++ b/examples/Metrics/src/HelloWorld/Function.cs @@ -41,6 +41,15 @@ public Function() var config = new DynamoDBContextConfig { Conversion = DynamoDBEntryConversion.V2 }; _dynamoDbContext = new DynamoDBContext(new AmazonDynamoDBClient(), config); } + + /// + /// Test constructor + /// + public Function(IDynamoDBContext dynamoDbContext, HttpClient httpClient) + { + _httpClient = httpClient; + _dynamoDbContext = dynamoDbContext; + } /// /// Lambda Handler @@ -141,17 +150,16 @@ public async Task FunctionHandler(APIGatewayProxyReques } /// - /// Saves the LookupRecord record in DynamoDB + /// Saves the lookup record in DynamoDB /// /// Instance of LookupRecord - /// LookupRecord - private static Task SaveRecordInDynamo(LookupRecord lookupRecord) + /// A Task that can be used to poll or wait for results, or both. + private static async Task SaveRecordInDynamo(LookupRecord lookupRecord) { try { Logger.LogInformation($"Saving record with id {lookupRecord.LookupId}"); - _dynamoDbContext?.SaveAsync(lookupRecord).Wait(); - return Task.FromResult(lookupRecord); + await _dynamoDbContext?.SaveAsync(lookupRecord)!; } catch (AmazonDynamoDBException e) { diff --git a/examples/Metrics/test/HelloWorld.Test/FunctionTest.cs b/examples/Metrics/test/HelloWorld.Test/FunctionTest.cs index dabd5dfd..1eafc033 100644 --- a/examples/Metrics/test/HelloWorld.Test/FunctionTest.cs +++ b/examples/Metrics/test/HelloWorld.Test/FunctionTest.cs @@ -1,11 +1,16 @@ using System; using System.Collections.Generic; +using System.Net; using System.Net.Http; using System.Text.Json; +using System.Threading; using System.Threading.Tasks; +using Amazon.DynamoDBv2.DataModel; using Xunit; using Amazon.Lambda.APIGatewayEvents; using Amazon.Lambda.TestUtilities; +using Moq; +using Moq.Protected; using Xunit.Abstractions; namespace HelloWorld.Tests @@ -13,31 +18,45 @@ namespace HelloWorld.Tests public class FunctionTest { private readonly ITestOutputHelper _testOutputHelper; - private static readonly HttpClient Client = new HttpClient(); public FunctionTest(ITestOutputHelper testOutputHelper) { _testOutputHelper = testOutputHelper; } - private static async Task GetCallingIP() - { - Client.DefaultRequestHeaders.Accept.Clear(); - Client.DefaultRequestHeaders.Add("User-Agent", "AWS Lambda .Net Client"); - - var stringTask = Client.GetStringAsync("http://checkip.amazonaws.com/") - .ConfigureAwait(continueOnCapturedContext: false); - - var msg = await stringTask; - return msg.Replace("\n", ""); - } - [Fact] public async Task TestHelloWorldFunctionHandler() { var requestId = Guid.NewGuid().ToString("D"); - var request = new APIGatewayProxyRequest() - { RequestContext = new APIGatewayProxyRequest.ProxyRequestContext() { RequestId = requestId } }; + var accountId = Guid.NewGuid().ToString("D"); + var location = "192.158. 1.38"; + Environment.SetEnvironmentVariable("POWERTOOLS_METRICS_NAMESPACE","AWSLambdaPowertools"); + + var dynamoDbContext = new Mock(); + var handlerMock = new Mock(); + handlerMock + .Protected() + .Setup>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny() + ) + .ReturnsAsync(new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent(location) + }) + .Verifiable(); + + var request = new APIGatewayProxyRequest + { + RequestContext = new APIGatewayProxyRequest.ProxyRequestContext + { + RequestId = requestId, + AccountId = accountId + } + }; + var context = new TestLambdaContext() { FunctionName = "PowertoolsLoggingSample-HelloWorldFunction-Gg8rhPwO7Wa1", @@ -45,8 +64,8 @@ public async Task TestHelloWorldFunctionHandler() MemoryLimitInMB = 215, AwsRequestId = Guid.NewGuid().ToString("D") }; - string location = GetCallingIP().Result; - Dictionary body = new Dictionary + + var body = new Dictionary { { "LookupId", requestId }, { "Greeting", "Hello AWS Lambda Powertools for .NET" }, @@ -60,7 +79,7 @@ public async Task TestHelloWorldFunctionHandler() Headers = new Dictionary { { "Content-Type", "application/json" } } }; - var function = new Function(); + var function = new Function(dynamoDbContext.Object, new HttpClient(handlerMock.Object)); var response = await function.FunctionHandler(request, context); _testOutputHelper.WriteLine("Lambda Response: \n" + response.Body); diff --git a/examples/Metrics/test/HelloWorld.Test/HelloWorld.Tests.csproj b/examples/Metrics/test/HelloWorld.Test/HelloWorld.Tests.csproj index 512a14d9..be3f49fd 100644 --- a/examples/Metrics/test/HelloWorld.Test/HelloWorld.Tests.csproj +++ b/examples/Metrics/test/HelloWorld.Test/HelloWorld.Tests.csproj @@ -6,7 +6,9 @@ + + diff --git a/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/XRayRecorder.cs b/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/XRayRecorder.cs index 144da9cd..51d511a1 100644 --- a/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/XRayRecorder.cs +++ b/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/XRayRecorder.cs @@ -39,17 +39,23 @@ internal class XRayRecorder : IXRayRecorder /// The instance. public static IXRayRecorder Instance => _instance ??= new XRayRecorder(); + /// + /// Checks whether current execution is in AWS Lambda. + /// + /// Returns true if current execution is in AWS Lambda. + private static readonly bool _isLambda = AWSXRayRecorder.IsLambda(); + /// /// Gets the emitter. /// /// The emitter. - public ISegmentEmitter Emitter => AWSXRayRecorder.Instance.Emitter; + public ISegmentEmitter Emitter => _isLambda ? AWSXRayRecorder.Instance.Emitter : null; /// /// Gets the streaming strategy. /// /// The streaming strategy. - public IStreamingStrategy StreamingStrategy => AWSXRayRecorder.Instance.StreamingStrategy; + public IStreamingStrategy StreamingStrategy => _isLambda ? AWSXRayRecorder.Instance.StreamingStrategy : null; /// /// Begins the subsegment. @@ -57,7 +63,8 @@ internal class XRayRecorder : IXRayRecorder /// The name. public void BeginSubsegment(string name) { - AWSXRayRecorder.Instance.BeginSubsegment(name); + if (_isLambda) + AWSXRayRecorder.Instance.BeginSubsegment(name); } /// @@ -66,7 +73,8 @@ public void BeginSubsegment(string name) /// The value. public void SetNamespace(string value) { - AWSXRayRecorder.Instance.SetNamespace(value); + if (_isLambda) + AWSXRayRecorder.Instance.SetNamespace(value); } /// @@ -76,7 +84,8 @@ public void SetNamespace(string value) /// The value. public void AddAnnotation(string key, object value) { - AWSXRayRecorder.Instance.AddAnnotation(key, value); + if (_isLambda) + AWSXRayRecorder.Instance.AddAnnotation(key, value); } /// @@ -87,7 +96,8 @@ public void AddAnnotation(string key, object value) /// The value. public void AddMetadata(string nameSpace, string key, object value) { - AWSXRayRecorder.Instance.AddMetadata(nameSpace, key, value); + if (_isLambda) + AWSXRayRecorder.Instance.AddMetadata(nameSpace, key, value); } /// @@ -95,7 +105,8 @@ public void AddMetadata(string nameSpace, string key, object value) /// public void EndSubsegment() { - AWSXRayRecorder.Instance.EndSubsegment(); + if (_isLambda) + AWSXRayRecorder.Instance.EndSubsegment(); } /// @@ -104,7 +115,9 @@ public void EndSubsegment() /// Entity. public Entity GetEntity() { - return AWSXRayRecorder.Instance.GetEntity(); + return _isLambda + ? AWSXRayRecorder.Instance.GetEntity() + : new Segment("Root"); } /// @@ -113,7 +126,8 @@ public Entity GetEntity() /// The entity. public void SetEntity(Entity entity) { - AWSXRayRecorder.Instance.SetEntity(entity); + if (_isLambda) + AWSXRayRecorder.Instance.SetEntity(entity); } /// @@ -122,7 +136,8 @@ public void SetEntity(Entity entity) /// The exception. public void AddException(Exception exception) { - AWSXRayRecorder.Instance.AddException(exception); + if (_isLambda) + AWSXRayRecorder.Instance.AddException(exception); } /// @@ -132,6 +147,7 @@ public void AddException(Exception exception) /// The value. public void AddHttpInformation(string key, object value) { - AWSXRayRecorder.Instance.AddHttpInformation(key, value); + if (_isLambda) + AWSXRayRecorder.Instance.AddHttpInformation(key, value); } } \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.Tracing/Tracing.cs b/libraries/src/AWS.Lambda.Powertools.Tracing/Tracing.cs index 2987047d..357987b0 100644 --- a/libraries/src/AWS.Lambda.Powertools.Tracing/Tracing.cs +++ b/libraries/src/AWS.Lambda.Powertools.Tracing/Tracing.cs @@ -212,11 +212,14 @@ public static void WithSubsegment(string nameSpace, string name, Entity entity, childSubsegment.IsInProgress = false; childSubsegment.Release(); childSubsegment.SetEndTimeToNow(); - if (childSubsegment.IsEmittable()) - XRayRecorder.Instance.Emitter.Send(childSubsegment.RootSegment); - else if (XRayRecorder.Instance.StreamingStrategy.ShouldStream(childSubsegment)) - XRayRecorder.Instance.StreamingStrategy.Stream(childSubsegment.RootSegment, - XRayRecorder.Instance.Emitter); + if (PowertoolsConfigurations.Instance.IsLambdaEnvironment) + { + if (childSubsegment.IsEmittable()) + XRayRecorder.Instance.Emitter.Send(childSubsegment.RootSegment); + else if (XRayRecorder.Instance.StreamingStrategy.ShouldStream(childSubsegment)) + XRayRecorder.Instance.StreamingStrategy.Stream(childSubsegment.RootSegment, + XRayRecorder.Instance.Emitter); + } } }