diff --git a/.autover/changes/16cc3c6a-8fa0-410b-ba57-74221abb086a.json b/.autover/changes/16cc3c6a-8fa0-410b-ba57-74221abb086a.json new file mode 100644 index 000000000..b49862a21 --- /dev/null +++ b/.autover/changes/16cc3c6a-8fa0-410b-ba57-74221abb086a.json @@ -0,0 +1,48 @@ +{ + "Projects": [ + { + "Name": "Amazon.Lambda.RuntimeSupport", + "Type": "Patch", + "ChangelogMessages": [ + "Add support for parameterized logging method with exception to global logger LambdaLogger in Amazon.Lambda.Core" + ] + }, + { + "Name": "Amazon.Lambda.Core", + "Type": "Minor", + "ChangelogMessages": [ + "Added log level version of the static logging functions on Amazon.Lambda.Core.LambdaLogger" + ] + } + { + "Name": "Amazon.Lambda.AspNetCoreServer", + "Type": "Patch", + "ChangelogMessages": [ + "Update Amazon.Lambda.Logging.AspNetCore dependency" + ] + }, + { + "Name": "Amazon.Lambda.AspNetCoreServer.Hosting", + "Type": "Patch", + "ChangelogMessages": [ + "Update Amazon.Lambda.Logging.AspNetCore dependency" + ] + }, + { + "Name": "Amazon.Lambda.Logging.AspNetCore", + "Type": "Major", + "ChangelogMessages": [ + "Add support Lambda log levels", + "Change build target from .NET Standard 2.0 to .NET 6 and NET 8 to match Amazon.Lambda.AspNetCoreServer" + ] + }, + { + "Name": "Amazon.Lambda.TestUtilities", + "Type": "Major", + "ChangelogMessages": [ + "Update Amazon.Lambda.TestUtitlies to have implementation of the newer logging methods", + "Change build target from .NET Standard 2.0 to .NET 6 and NET 8 to match Amazon.Lambda.AspNetCoreServer" + ] + } + ] +} \ No newline at end of file diff --git a/Libraries/src/Amazon.Lambda.Core/LambdaLogger.cs b/Libraries/src/Amazon.Lambda.Core/LambdaLogger.cs index c6b612bcf..8cb4d128e 100644 --- a/Libraries/src/Amazon.Lambda.Core/LambdaLogger.cs +++ b/Libraries/src/Amazon.Lambda.Core/LambdaLogger.cs @@ -42,6 +42,7 @@ public static void Log(string message) // value with an Action that directs the logging into its logging system. #pragma warning disable IDE0044 // Add readonly modifier private static Action _loggingWithLevelAction = LogWithLevelToConsole; + private static Action _loggingWithLevelAndExceptionAction = LogWithLevelAndExceptionToConsole; #pragma warning restore IDE0044 // Add readonly modifier // Logs message to console @@ -65,6 +66,15 @@ private static void LogWithLevelToConsole(string level, string message, params o Console.WriteLine(sb.ToString()); } + private static void LogWithLevelAndExceptionToConsole(string level, Exception exception, string message, params object[] args) + { + // Formatting here is not important, it is used for debugging Amazon.Lambda.Core only. + // In a real scenario Amazon.Lambda.RuntimeSupport will change the value of _loggingWithLevelAction + // to an Action inside it's logging system to handle the real formatting. + LogWithLevelToConsole(level, message, args); + Console.WriteLine(exception); + } + private const string ParameterizedPreviewMessage = "This method has been mark as preview till the Lambda .NET Managed runtime has been updated with the backing implementation of this method. " + "It is possible to use this method while in preview if the Lambda function is deployed as an executable and uses the latest version of Amazon.Lambda.RuntimeSupport."; @@ -78,7 +88,6 @@ private static void LogWithLevelToConsole(string level, string message, params o /// The log level of the message /// Message to log. The message may have format arguments. /// Arguments to format the message with. - [RequiresPreviewFeatures(ParameterizedPreviewMessage)] public static void Log(string level, string message, params object[] args) { _loggingWithLevelAction(level, message, args); @@ -93,8 +102,36 @@ public static void Log(string level, string message, params object[] args) /// The log level of the message /// Message to log. The message may have format arguments. /// Arguments to format the message with. - [RequiresPreviewFeatures(ParameterizedPreviewMessage)] public static void Log(LogLevel level, string message, params object[] args) => Log(level.ToString(), message, args); + + /// + /// Logs a message to AWS CloudWatch Logs. + /// + /// Logging will not be done: + /// If the role provided to the function does not have sufficient permissions. + /// + /// The log level of the message + /// Exception to include with the logging. + /// Message to log. The message may have format arguments. + /// Arguments to format the message with. + [RequiresPreviewFeatures(ParameterizedPreviewMessage)] + public static void Log(string level, Exception exception, string message, params object[] args) + { + _loggingWithLevelAndExceptionAction(level, exception, message, args); + } + + /// + /// Logs a message to AWS CloudWatch Logs. + /// + /// Logging will not be done: + /// If the role provided to the function does not have sufficient permissions. + /// + /// The log level of the message + /// Exception to include with the logging. + /// Message to log. The message may have format arguments. + /// Arguments to format the message with. + [RequiresPreviewFeatures(ParameterizedPreviewMessage)] + public static void Log(LogLevel level, Exception exception, string message, params object[] args) => Log(level.ToString(), exception, message, args); #endif } } diff --git a/Libraries/src/Amazon.Lambda.Logging.AspNetCore/Amazon.Lambda.Logging.AspNetCore.csproj b/Libraries/src/Amazon.Lambda.Logging.AspNetCore/Amazon.Lambda.Logging.AspNetCore.csproj index 7d394b38a..86a94e2f5 100644 --- a/Libraries/src/Amazon.Lambda.Logging.AspNetCore/Amazon.Lambda.Logging.AspNetCore.csproj +++ b/Libraries/src/Amazon.Lambda.Logging.AspNetCore/Amazon.Lambda.Logging.AspNetCore.csproj @@ -4,7 +4,7 @@ Amazon Lambda .NET Core support - Logging ASP.NET Core package. - netstandard2.0 + net6.0;net8.0 Amazon.Lambda.Logging.AspNetCore 3.1.1 Amazon.Lambda.Logging.AspNetCore diff --git a/Libraries/src/Amazon.Lambda.Logging.AspNetCore/LambdaILogger.cs b/Libraries/src/Amazon.Lambda.Logging.AspNetCore/LambdaILogger.cs index 7e6de8bab..decfeb407 100644 --- a/Libraries/src/Amazon.Lambda.Logging.AspNetCore/LambdaILogger.cs +++ b/Libraries/src/Amazon.Lambda.Logging.AspNetCore/LambdaILogger.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace Microsoft.Extensions.Logging @@ -28,54 +28,85 @@ public bool IsEnabled(LogLevel logLevel) _options.Filter == null || _options.Filter(_categoryName, logLevel)); } - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) - { - if (formatter == null) - { - throw new ArgumentNullException(nameof(formatter)); - } - - if (!IsEnabled(logLevel)) - { - return; - } - - // Format of the logged text, optional components are in {} - // {[LogLevel] }{ => Scopes : }{Category: }{EventId: }MessageText {Exception}{\n} - - var components = new List(4); - if (_options.IncludeLogLevel) - { - components.Add($"[{logLevel}]"); - } - - GetScopeInformation(components); - - if (_options.IncludeCategory) - { - components.Add($"{_categoryName}:"); - } - if (_options.IncludeEventId) - { - components.Add($"[{eventId}]:"); - } - - var text = formatter.Invoke(state, exception); - components.Add(text); - - if (_options.IncludeException) - { - components.Add($"{exception}"); - } - if (_options.IncludeNewline) - { - components.Add(Environment.NewLine); - } - - var finalText = string.Join(" ", components); - Amazon.Lambda.Core.LambdaLogger.Log(finalText); - } + + /// + /// The Log method called by the ILogger framework to log message to logger's target. In the Lambda case the formatted logging will be + /// sent to the Amazon.Lambda.Core.LambdaLogger's Log method. + /// + /// + /// + /// + /// + /// + /// + /// + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (formatter == null) + { + throw new ArgumentNullException(nameof(formatter)); + } + + if (!IsEnabled(logLevel)) + { + return; + } + + var components = new List(4); + if (_options.IncludeLogLevel) + { + components.Add($"[{logLevel}]"); + } + + GetScopeInformation(components); + + if (_options.IncludeCategory) + { + components.Add($"{_categoryName}:"); + } + if (_options.IncludeEventId) + { + components.Add($"[{eventId}]:"); + } + + var text = formatter.Invoke(state, exception); + components.Add(text); + + if (_options.IncludeException) + { + components.Add($"{exception}"); + } + if (_options.IncludeNewline) + { + components.Add(Environment.NewLine); + } + + var finalText = string.Join(" ", components); + + var lambdaLogLevel = ConvertLogLevel(logLevel); + Amazon.Lambda.Core.LambdaLogger.Log(lambdaLogLevel, finalText); + } + + private static Amazon.Lambda.Core.LogLevel ConvertLogLevel(LogLevel logLevel) + { + switch (logLevel) + { + case LogLevel.Trace: + return Amazon.Lambda.Core.LogLevel.Trace; + case LogLevel.Debug: + return Amazon.Lambda.Core.LogLevel.Debug; + case LogLevel.Information: + return Amazon.Lambda.Core.LogLevel.Information; + case LogLevel.Warning: + return Amazon.Lambda.Core.LogLevel.Warning; + case LogLevel.Error: + return Amazon.Lambda.Core.LogLevel.Error; + case LogLevel.Critical: + return Amazon.Lambda.Core.LogLevel.Critical; + default: + return Amazon.Lambda.Core.LogLevel.Information; + } + } private void GetScopeInformation(List logMessageComponents) { diff --git a/Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/UserCodeLoader.cs b/Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/UserCodeLoader.cs index 946ffbe7c..ff61ff2f5 100644 --- a/Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/UserCodeLoader.cs +++ b/Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/UserCodeLoader.cs @@ -35,6 +35,8 @@ internal class UserCodeLoader { private const string UserInvokeException = "An exception occurred while invoking customer handler."; private const string LambdaLoggingActionFieldName = "_loggingAction"; + private const string LambdaLoggingWithLevelActionFieldName = "_loggingWithLevelAction"; + private const string LambdaLoggingWithLevelAndExceptionActionFieldName = "_loggingWithLevelAndExceptionAction"; internal const string LambdaCoreAssemblyName = "Amazon.Lambda.Core"; @@ -148,6 +150,13 @@ public void Invoke(Stream lambdaData, ILambdaContext lambdaContext, Stream outSt _invokeDelegate(lambdaData, lambdaContext, outStream); } + /// + /// Sets the backing logger action field in Amazon.Logging.Core to redirect logs into Amazon.Lambda.RuntimeSupport. + /// + /// + /// + /// + /// internal static void SetCustomerLoggerLogAction(Assembly coreAssembly, Action customerLoggingAction, InternalLogger internalLogger) { if (coreAssembly == null) @@ -186,6 +195,97 @@ internal static void SetCustomerLoggerLogAction(Assembly coreAssembly, Action + /// Sets the backing logger action field in Amazon.Logging.Core to redirect logs into Amazon.Lambda.RuntimeSupport. + /// + /// + /// + /// + /// + + internal static void SetCustomerLoggerLogAction(Assembly coreAssembly, Action loggingWithLevelAction, InternalLogger internalLogger) + { + if (coreAssembly == null) + { + throw new ArgumentNullException(nameof(coreAssembly)); + } + + if (loggingWithLevelAction == null) + { + throw new ArgumentNullException(nameof(loggingWithLevelAction)); + } + + internalLogger.LogDebug($"UCL : Retrieving type '{Types.LambdaLoggerTypeName}'"); + var lambdaILoggerType = coreAssembly.GetType(Types.LambdaLoggerTypeName); + if (lambdaILoggerType == null) + { + throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.Internal.UnableToLocateType, Types.LambdaLoggerTypeName); + } + + internalLogger.LogDebug($"UCL : Retrieving field '{LambdaLoggingWithLevelActionFieldName}'"); + var loggingActionField = lambdaILoggerType.GetTypeInfo().GetField(LambdaLoggingWithLevelActionFieldName, BindingFlags.NonPublic | BindingFlags.Static); + if (loggingActionField == null) + { + throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.Internal.UnableToRetrieveField, LambdaLoggingWithLevelActionFieldName, Types.LambdaLoggerTypeName); + } + + internalLogger.LogDebug($"UCL : Setting field '{LambdaLoggingWithLevelActionFieldName}'"); + try + { + loggingActionField.SetValue(null, loggingWithLevelAction); + } + catch (Exception e) + { + throw LambdaExceptions.ValidationException(e, Errors.UserCodeLoader.Internal.UnableToSetField, + Types.LambdaLoggerTypeName, LambdaLoggingWithLevelActionFieldName); + } + } + + /// + /// Sets the backing logger action field in Amazon.Logging.Core to redirect logs into Amazon.Lambda.RuntimeSupport. + /// + /// + /// + /// + /// + internal static void SetCustomerLoggerLogAction(Assembly coreAssembly, Action loggingWithAndExceptionLevelAction, InternalLogger internalLogger) + { + if (coreAssembly == null) + { + throw new ArgumentNullException(nameof(coreAssembly)); + } + + if (loggingWithAndExceptionLevelAction == null) + { + throw new ArgumentNullException(nameof(loggingWithAndExceptionLevelAction)); + } + + internalLogger.LogDebug($"UCL : Retrieving type '{Types.LambdaLoggerTypeName}'"); + var lambdaILoggerType = coreAssembly.GetType(Types.LambdaLoggerTypeName); + if (lambdaILoggerType == null) + { + throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.Internal.UnableToLocateType, Types.LambdaLoggerTypeName); + } + + internalLogger.LogDebug($"UCL : Retrieving field '{LambdaLoggingWithLevelAndExceptionActionFieldName}'"); + var loggingActionField = lambdaILoggerType.GetTypeInfo().GetField(LambdaLoggingWithLevelAndExceptionActionFieldName, BindingFlags.NonPublic | BindingFlags.Static); + if (loggingActionField == null) + { + throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.Internal.UnableToRetrieveField, LambdaLoggingWithLevelAndExceptionActionFieldName, Types.LambdaLoggerTypeName); + } + + internalLogger.LogDebug($"UCL : Setting field '{LambdaLoggingWithLevelAndExceptionActionFieldName}'"); + try + { + loggingActionField.SetValue(null, loggingWithAndExceptionLevelAction); + } + catch (Exception e) + { + throw LambdaExceptions.ValidationException(e, Errors.UserCodeLoader.Internal.UnableToSetField, + Types.LambdaLoggerTypeName, LambdaLoggingWithLevelAndExceptionActionFieldName); + } + } + /// /// Constructs customer-specified serializer, specified either on the method, /// the assembly, or not specified at all. diff --git a/Libraries/src/Amazon.Lambda.RuntimeSupport/Helpers/ConsoleLoggerWriter.cs b/Libraries/src/Amazon.Lambda.RuntimeSupport/Helpers/ConsoleLoggerWriter.cs index 18417e562..95004dd2e 100644 --- a/Libraries/src/Amazon.Lambda.RuntimeSupport/Helpers/ConsoleLoggerWriter.cs +++ b/Libraries/src/Amazon.Lambda.RuntimeSupport/Helpers/ConsoleLoggerWriter.cs @@ -247,7 +247,7 @@ private void ConfigureLoggingActionField() var loggingActionField = lambdaLoggerType.GetTypeInfo().GetField("_loggingAction", BindingFlags.NonPublic | BindingFlags.Static); if (loggingActionField != null) { - Action loggingAction = (message => FormattedWriteLine(null, message)); + Action loggingAction = message => FormattedWriteLine(null, message); loggingActionField.SetValue(null, loggingAction); } @@ -255,9 +255,15 @@ private void ConfigureLoggingActionField() var loggingWithLevelActionField = lambdaLoggerType.GetTypeInfo().GetField("_loggingWithLevelAction", BindingFlags.NonPublic | BindingFlags.Static); if (loggingWithLevelActionField != null) { - Action loggingWithLevelAction = ((level, message, args) => FormattedWriteLine(level, message, args)); + Action loggingWithLevelAction = (level, message, args) => FormattedWriteLine(level, message, args); loggingWithLevelActionField.SetValue(null, loggingWithLevelAction); + } + var loggingWithLevelAndExceptionActionField = lambdaLoggerType.GetTypeInfo().GetField("_loggingWithLevelAndExceptionAction", BindingFlags.NonPublic | BindingFlags.Static); + if (loggingWithLevelAndExceptionActionField != null) + { + Action loggingWithLevelAndExceptionAction = (level, exception, message, args) => FormattedWriteLine(level, exception, message, args); + loggingWithLevelAndExceptionActionField.SetValue(null, loggingWithLevelAndExceptionAction); } } diff --git a/Libraries/src/Amazon.Lambda.TestUtilities/Amazon.Lambda.TestUtilities.csproj b/Libraries/src/Amazon.Lambda.TestUtilities/Amazon.Lambda.TestUtilities.csproj index f15727a30..eedc42127 100644 --- a/Libraries/src/Amazon.Lambda.TestUtilities/Amazon.Lambda.TestUtilities.csproj +++ b/Libraries/src/Amazon.Lambda.TestUtilities/Amazon.Lambda.TestUtilities.csproj @@ -4,7 +4,7 @@ Amazon.Lambda.TestUtilties includes stub implementations of interfaces defined in Amazon.Lambda.Core and helper methods. - netstandard2.0 + net6.0;net8.0 Amazon.Lambda.TestUtilities 2.0.0 Amazon.Lambda.TestUtilities diff --git a/Libraries/src/Amazon.Lambda.TestUtilities/TestLambdaLogger.cs b/Libraries/src/Amazon.Lambda.TestUtilities/TestLambdaLogger.cs index 20c29564a..1e46b09ca 100644 --- a/Libraries/src/Amazon.Lambda.TestUtilities/TestLambdaLogger.cs +++ b/Libraries/src/Amazon.Lambda.TestUtilities/TestLambdaLogger.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -37,5 +37,71 @@ public void LogLine(string message) Buffer.AppendLine(message); Console.WriteLine(message); } + + /// + /// Write log messages to the console and the Buffer. + /// + /// + /// + public void Log(string level, string message) + { + var formmattedString = $"{level}: {message}"; + Buffer.AppendLine(formmattedString); + Console.WriteLine(formmattedString); + } + + /// + /// Write log messages to the console and the Buffer. + /// + /// + /// + /// + public void Log(string level, string message, params object[] args) + { + var builder = new StringBuilder(); + builder.Append($"{level}: {message}"); + if (args != null && args.Length > 0) + { + builder.AppendLine(); + foreach (var arg in args) + { + builder.AppendLine($"\t{arg}"); + } + } + + var formmattedString = builder.ToString(); + Buffer.AppendLine(formmattedString); + Console.WriteLine(formmattedString); + } + + /// + /// Write log messages to the console and the Buffer. + /// + /// + /// + /// + /// + public void Log(string level, Exception exception, string message, params object[] args) + { + var builder = new StringBuilder(); + builder.Append($"{level}: {message}"); + if (args != null && args.Length > 0) + { + builder.AppendLine(); + foreach (var arg in args) + { + builder.AppendLine($"\t{arg}"); + } + } + if (exception != null) + { + builder.AppendLine(); + builder.AppendLine(exception.ToString()); + } + + var formmattedString = builder.ToString(); + Buffer.AppendLine(formmattedString); + Console.WriteLine(formmattedString); + } } } diff --git a/Libraries/test/Amazon.Lambda.Core.Tests/Amazon.Lambda.Core.Tests.csproj b/Libraries/test/Amazon.Lambda.Core.Tests/Amazon.Lambda.Core.Tests.csproj index f06e3ec29..0c1665b85 100644 --- a/Libraries/test/Amazon.Lambda.Core.Tests/Amazon.Lambda.Core.Tests.csproj +++ b/Libraries/test/Amazon.Lambda.Core.Tests/Amazon.Lambda.Core.Tests.csproj @@ -1,7 +1,7 @@  - net6.0 + net8.0 Amazon.Lambda.Core.Tests Amazon.Lambda.Core.Tests true @@ -9,11 +9,12 @@ + - + diff --git a/Libraries/test/Amazon.Lambda.Core.Tests/TestUtilitiesLoggingTest.cs b/Libraries/test/Amazon.Lambda.Core.Tests/TestUtilitiesLoggingTest.cs new file mode 100644 index 000000000..e1b39c830 --- /dev/null +++ b/Libraries/test/Amazon.Lambda.Core.Tests/TestUtilitiesLoggingTest.cs @@ -0,0 +1,50 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Xunit; +using System.Linq; +using Amazon.Lambda.TestUtilities; +using Amazon.Lambda.Core; + +namespace Amazon.Lambda.Tests +{ + public class TestUtilitiesLoggingTest + { + [Fact] + public void LogWithLevelAndMessage() + { + ILambdaLogger logger = new TestLambdaLogger(); + logger.Log(LogLevel.Warning, "This is a warning"); + + Assert.Contains("Warning: This is a warning", ((TestLambdaLogger)logger).Buffer.ToString()); + } + + [Fact] + public void LogWithLevelAndMessageAndParameters() + { + ILambdaLogger logger = new TestLambdaLogger(); + logger.Log(LogLevel.Warning, "This is {name}", "garp"); + + Assert.Contains("Warning: This is {name}", ((TestLambdaLogger)logger).Buffer.ToString()); + Assert.Contains("\tgarp", ((TestLambdaLogger)logger).Buffer.ToString()); + } + + [Fact] + public void LogWithLevelAndMessageAndParametersAndExecption() + { + ILambdaLogger logger = new TestLambdaLogger(); + + var exception = new ArgumentException("Bad Name"); + logger.Log(LogLevel.Warning, exception, "This is {name}", "garp"); + + + Assert.Contains("Warning: This is {name}", ((TestLambdaLogger)logger).Buffer.ToString()); + Assert.Contains("\tgarp", ((TestLambdaLogger)logger).Buffer.ToString()); + Assert.Contains("ArgumentException", ((TestLambdaLogger)logger).Buffer.ToString()); + } + } +} diff --git a/Libraries/test/Amazon.Lambda.Logging.AspNetCore.Tests/LoggingTests.cs b/Libraries/test/Amazon.Lambda.Logging.AspNetCore.Tests/LoggingTests.cs index 35c46cb2b..5a8bbecde 100644 --- a/Libraries/test/Amazon.Lambda.Logging.AspNetCore.Tests/LoggingTests.cs +++ b/Libraries/test/Amazon.Lambda.Logging.AspNetCore.Tests/LoggingTests.cs @@ -76,6 +76,9 @@ public void TestConfiguration() // check that there are no unexpected strings in the text Assert.DoesNotContain(SHOULD_NOT_APPEAR, text); + // Confirm log level was written to log + Assert.Contains("Critical:", text); + // check that all expected strings are in the text for (int i = 0; i < count; i++) { @@ -552,7 +555,20 @@ private static void ConnectLoggingActionToLogger(Action loggingAction) Assert.NotNull(loggingActionField); loggingActionField.SetValue(null, loggingAction); - } + + Action loggingWithLevelAction = (level, message, parameters) => { + var formattedMessage = $"{level}: {message}: parameter count: {parameters?.Length}"; + loggingAction(formattedMessage); + }; + + var loggingWithLevelActionField = lambdaLoggerType + .GetTypeInfo() + .GetField("_loggingWithLevelAction", BindingFlags.NonPublic | BindingFlags.Static); + Assert.NotNull(loggingActionField); + + loggingWithLevelActionField.SetValue(null, loggingWithLevelAction); + } + private static int CountOccurences(string text, string substring) { int occurences = 0; diff --git a/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.IntegrationTests/BaseCustomRuntimeTest.cs b/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.IntegrationTests/BaseCustomRuntimeTest.cs index 5b2741b5f..e8eb5225d 100644 --- a/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.IntegrationTests/BaseCustomRuntimeTest.cs +++ b/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.IntegrationTests/BaseCustomRuntimeTest.cs @@ -1,4 +1,4 @@ -using Amazon.IdentityManagement; +using Amazon.IdentityManagement; using Amazon.IdentityManagement.Model; using Amazon.Lambda.Model; using Amazon.S3; @@ -267,7 +267,7 @@ protected async Task CreateFunctionAsync(IAmazonLambda lambdaClient, string buck S3Bucket = bucketName, S3Key = DeploymentZipKey }, - Handler = this.Handler, + Handler = Handler, MemorySize = FUNCTION_MEMORY_MB, Timeout = 30, Runtime = Runtime.Dotnet6, diff --git a/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.IntegrationTests/CustomRuntimeTests.cs b/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.IntegrationTests/CustomRuntimeTests.cs index 387245e63..066428f21 100644 --- a/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.IntegrationTests/CustomRuntimeTests.cs +++ b/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.IntegrationTests/CustomRuntimeTests.cs @@ -106,6 +106,7 @@ protected virtual async Task TestAllHandlersAsync() await RunTestSuccessAsync(lambdaClient, "UnintendedDisposeTest", "not-used", "UnintendedDisposeTest-SUCCESS"); await RunTestSuccessAsync(lambdaClient, "LoggingStressTest", "not-used", "LoggingStressTest-success"); + await RunGlobalLoggingWithExceptionTestAsync(lambdaClient, "GlobalLoggingWithExceptionTest"); await RunGlobalLoggingTestAsync(lambdaClient, "GlobalLoggingTest"); await RunJsonLoggingWithUnhandledExceptionAsync(lambdaClient); @@ -330,6 +331,21 @@ private async Task RunGlobalLoggingTestAsync(AmazonLambdaClient lambdaClient, st Assert.Contains("This is a global log message with foobar as an argument", log); } + private async Task RunGlobalLoggingWithExceptionTestAsync(AmazonLambdaClient lambdaClient, string handler) + { + await UpdateHandlerAsync(lambdaClient, handler); + + var invokeResponse = await InvokeFunctionAsync(lambdaClient, JsonConvert.SerializeObject("")); + Assert.True(invokeResponse.HttpStatusCode == System.Net.HttpStatusCode.OK); + Assert.True(invokeResponse.FunctionError == null); + + var log = System.Text.UTF8Encoding.UTF8.GetString(Convert.FromBase64String(invokeResponse.LogResult)); + + Assert.Contains("This is a global log message with foobar as an argument", log); + Assert.Contains("ArgumentException", log); + Assert.Contains("This is the wrong argument", log); + } + private async Task RunUnformattedLoggingTestAsync(AmazonLambdaClient lambdaClient, string handler) { var environmentVariables = new Dictionary(); diff --git a/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/HandlerTests.cs b/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/HandlerTests.cs index db99d880b..42e0d6957 100644 --- a/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/HandlerTests.cs +++ b/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/HandlerTests.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). @@ -393,9 +393,10 @@ private async Task ExecHandlerAsync(string handler, string dataIn Client = testRuntimeApiClient }; - var loggerAction = actionWriter.ToLoggingAction(); var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(UserCodeLoader.LambdaCoreAssemblyName)); - UserCodeLoader.SetCustomerLoggerLogAction(assembly, loggerAction, _internalLogger); + UserCodeLoader.SetCustomerLoggerLogAction(assembly, actionWriter.ToLoggingAction(), _internalLogger); + UserCodeLoader.SetCustomerLoggerLogAction(assembly, actionWriter.ToLoggingWithLevelAction(), _internalLogger); + UserCodeLoader.SetCustomerLoggerLogAction(assembly, actionWriter.ToLoggingWithLevelAndExceptionAction(), _internalLogger); if (assertLoggedByInitialize != null) { diff --git a/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/TestHelpers/StringWriterExtensions.cs b/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/TestHelpers/StringWriterExtensions.cs index fdd328b2e..6658363dc 100644 --- a/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/TestHelpers/StringWriterExtensions.cs +++ b/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/TestHelpers/StringWriterExtensions.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). @@ -67,9 +67,27 @@ public static Action ToLoggingAction(this TextWriter writer) return writer.WriteLine; } + public static Action ToLoggingWithLevelAction(this TextWriter writer) + { + return (l, m, p) => + { + var formattedMessage = $"{l}: {m}"; + writer.WriteLine(formattedMessage); + }; + } + + public static Action ToLoggingWithLevelAndExceptionAction(this TextWriter writer) + { + return (l, e, m, p) => + { + var formattedMessage = $"{l}: {m}\n{e}"; + writer.WriteLine(formattedMessage); + }; + } + public static Action ToLoggingAction(this ITestOutputHelper writer) { return writer.WriteLine; } } -} \ No newline at end of file +} diff --git a/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/CustomRuntimeFunctionTest/CustomRuntimeFunction.cs b/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/CustomRuntimeFunctionTest/CustomRuntimeFunction.cs index 7cd6ec3b0..6d9c80f91 100644 --- a/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/CustomRuntimeFunctionTest/CustomRuntimeFunction.cs +++ b/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/CustomRuntimeFunctionTest/CustomRuntimeFunction.cs @@ -51,6 +51,9 @@ private static async Task Main(string[] args) case nameof(GlobalLoggingTest): bootstrap = new LambdaBootstrap(GlobalLoggingTest); break; + case nameof(GlobalLoggingWithExceptionTest): + bootstrap = new LambdaBootstrap(GlobalLoggingWithExceptionTest); + break; case nameof(LoggingTest): bootstrap = new LambdaBootstrap(LoggingTest); break; @@ -172,12 +175,18 @@ Task UseLoggerAsync() return Task.FromResult(GetInvocationResponse(nameof(LoggingStressTest), "success")); } + private static Task GlobalLoggingWithExceptionTest(InvocationRequest invocation) + { +#pragma warning disable CA2252 + var exception = new ArgumentException("This is the wrong argument"); + LambdaLogger.Log(LogLevel.Error, exception, "This is a global log message with {argument} as an argument", "foobar"); +#pragma warning restore CA2252 + return Task.FromResult(GetInvocationResponse(nameof(GlobalLoggingWithExceptionTest), true)); + } private static Task GlobalLoggingTest(InvocationRequest invocation) { -#pragma warning disable CA2252 LambdaLogger.Log(LogLevel.Information, "This is a global log message with {argument} as an argument", "foobar"); -#pragma warning restore CA2252 return Task.FromResult(GetInvocationResponse(nameof(GlobalLoggingTest), true)); } diff --git a/Libraries/test/TestFunctionFSharp/TestFunctionCSharp/TestFunctionCSharp.csproj b/Libraries/test/TestFunctionFSharp/TestFunctionCSharp/TestFunctionCSharp.csproj index 47b6d25ee..a2177f269 100644 --- a/Libraries/test/TestFunctionFSharp/TestFunctionCSharp/TestFunctionCSharp.csproj +++ b/Libraries/test/TestFunctionFSharp/TestFunctionCSharp/TestFunctionCSharp.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net8.0 TestFunctionCSharp Library false diff --git a/Libraries/test/TestFunctionFSharp/TestFunctionFSharp/TestFunctionFSharp.fsproj b/Libraries/test/TestFunctionFSharp/TestFunctionFSharp/TestFunctionFSharp.fsproj index 6ed1d07f5..094d04fd7 100644 --- a/Libraries/test/TestFunctionFSharp/TestFunctionFSharp/TestFunctionFSharp.fsproj +++ b/Libraries/test/TestFunctionFSharp/TestFunctionFSharp/TestFunctionFSharp.fsproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net8.0 TestFunctionFSharp Library false