From 158f05b88b40b4b6649504ed8bbccbbb042b5b70 Mon Sep 17 00:00:00 2001 From: Amir Khairalomoum Date: Fri, 14 Apr 2023 16:54:12 +0100 Subject: [PATCH 1/4] change extra keys logger to accept any type and add Extra when keyname is not specified --- docs/core/logging.md | 81 ++++++++++++++++-- .../Internal/LoggingConstants.cs | 5 ++ .../Internal/PowertoolsLogger.cs | 26 +++++- .../AWS.Lambda.Powertools.Logging/Logger.cs | 56 ++++++------- .../LoggerExtensions.cs | 67 ++++++++------- .../PowertoolsLoggerTest.cs | 84 +++++++++++++++++-- 6 files changed, 241 insertions(+), 78 deletions(-) diff --git a/docs/core/logging.md b/docs/core/logging.md index 956515c3..7173af78 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -311,7 +311,7 @@ It accepts any dictionary, and all keyword arguments will be added as part of th === "Function.cs" - ```c# hl_lines="16" + ```c# hl_lines="15 18 27" /** * Handler for requests to Lambda function. */ @@ -323,16 +323,81 @@ It accepts any dictionary, and all keyword arguments will be added as part of th { var requestContextRequestId = apigwProxyEvent.RequestContext.RequestId; - var lookupId = new Dictionary() + var lookupInfo = new { LookupId = requestContextRequestId }; + + // Simply pass an object for logging additional data + Logger.LogInformation(lookupInfo, "This is a log with additional data"); + + // Specify the key name for extra additional data + Logger.LogInformation(("LookupInfo", lookupInfo), "This is a log with an extra variable"); + + var extraKeys = new Dictionary() { - { "LookupId", requestContextRequestId } + { "LookupInfo", lookupInfo }, + { "CorrelationIds", new { MyCorrelationId = "correlation_id_value" } } }; - // Appended keys are added to all subsequent log entries in the current execution. - // Call this method as early as possible in the Lambda handler. - // Typically this is value would be passed into the function via the event. - // Set the ClearState = true to force the removal of keys across invocations, - Logger.AppendKeys(lookupId); + // Specify the key name for extra additional data + Logger.LogInformation(extraKeys, "This is a log with multiple extra variables"); + } + ``` + +=== "Example CloudWatch Logs excerpt" + + ```json hl_lines="4-6 21-23 38-43" + { + "cold_start": true, + "xray_trace_id": "1-61b7add4-66532bb81441e1b060389429", + "extra": { + "lookup_id": "4c50eace-8b1e-43d3-92ba-0efacf5d1625" + }, + "function_name": "test", + "function_version": "$LATEST", + "function_memory_size": 128, + "function_arn": "arn:aws:lambda:eu-west-1:12345678910:function:test", + "function_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72", + "timestamp": "2021-12-13T20:32:22.5774262Z", + "level": "Information", + "service": "lambda-example", + "name": "AWS.Lambda.Powertools.Logging.Logger", + "message": "This is a log with additional data", + } + { + "cold_start": true, + "xray_trace_id": "1-61b7add4-66532bb81441e1b060389429", + "lookup_info": { + "lookup_id": "4c50eace-8b1e-43d3-92ba-0efacf5d1625" + }, + "function_name": "test", + "function_version": "$LATEST", + "function_memory_size": 128, + "function_arn": "arn:aws:lambda:eu-west-1:12345678910:function:test", + "function_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72", + "timestamp": "2021-12-13T20:32:22.5774262Z", + "level": "Information", + "service": "lambda-example", + "name": "AWS.Lambda.Powertools.Logging.Logger", + "message": "This is a log with an extra variable", + } + { + "cold_start": true, + "xray_trace_id": "1-61b7add4-66532bb81441e1b060389429", + "lookup_info": { + "lookup_id": "4c50eace-8b1e-43d3-92ba-0efacf5d1625" + }, + "correlation_ids": { + "my_correlation_id": "correlation_id_value" + }, + "function_name": "test", + "function_version": "$LATEST", + "function_memory_size": 128, + "function_arn": "arn:aws:lambda:eu-west-1:12345678910:function:test", + "function_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72", + "timestamp": "2021-12-13T20:32:22.5774262Z", + "level": "Information", + "service": "lambda-example", + "name": "AWS.Lambda.Powertools.Logging.Logger", + "message": "This is a log with multiple extra variables", } ``` diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingConstants.cs b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingConstants.cs index 1ff29c35..78993559 100644 --- a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingConstants.cs +++ b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingConstants.cs @@ -111,4 +111,9 @@ internal static class LoggingConstants /// Constant for key exception /// internal const string KeyException = "Exception"; + + /// + /// Constant for key extra + /// + internal const string KeyExtra = "Extra"; } \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsLogger.cs b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsLogger.cs index c45a7515..ba1d0694 100644 --- a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsLogger.cs +++ b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsLogger.cs @@ -16,6 +16,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Text.Encodings.Web; using System.Text.Json; using AWS.Lambda.Powertools.Common; @@ -163,12 +164,29 @@ private static Dictionary GetScopeKeys(TState state) } break; } + case KeyValuePair(var key, var value): + { + if (!string.IsNullOrWhiteSpace(key)) + keys.TryAdd(key, value); + break; + } + case KeyValuePair(var key, var value): + { + if (!string.IsNullOrWhiteSpace(key)) + keys.TryAdd(key, value); + break; + } + case ITuple pair: + { + if (pair.Length == 2 && pair[0] is string key && !string.IsNullOrWhiteSpace(key)) + keys.TryAdd(key, pair[1]); + else + keys.TryAdd(LoggingConstants.KeyExtra, state); + break; + } default: { - foreach (var property in state.GetType().GetProperties()) - { - keys.TryAdd(property.Name, property.GetValue(state)); - } + keys.TryAdd(LoggingConstants.KeyExtra, state); break; } } diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/Logger.cs b/libraries/src/AWS.Lambda.Powertools.Logging/Logger.cs index 0989abac..a5cadb85 100644 --- a/libraries/src/AWS.Lambda.Powertools.Logging/Logger.cs +++ b/libraries/src/AWS.Lambda.Powertools.Logging/Logger.cs @@ -768,7 +768,7 @@ public static void Log(LogLevel logLevel, Exception exception) /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogDebug(extraKeys, 0, exception, "Error while processing request from {Address}", address) - public static void LogDebug(T extraKeys, EventId eventId, Exception exception, string message, params object[] args) where T : class + public static void LogDebug(T extraKeys, EventId eventId, Exception exception, string message, params object[] args) { LoggerInstance.LogDebug(extraKeys, eventId, exception, message, args); } @@ -781,7 +781,7 @@ public static void LogDebug(T extraKeys, EventId eventId, Exception exception /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogDebug(extraKeys, 0, "Processing request from {Address}", address) - public static void LogDebug(T extraKeys, EventId eventId, string message, params object[] args) where T : class + public static void LogDebug(T extraKeys, EventId eventId, string message, params object[] args) { LoggerInstance.LogDebug(extraKeys, eventId, message, args); } @@ -794,7 +794,7 @@ public static void LogDebug(T extraKeys, EventId eventId, string message, par /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogDebug(extraKeys, exception, "Error while processing request from {Address}", address) - public static void LogDebug(T extraKeys, Exception exception, string message, params object[] args) where T : class + public static void LogDebug(T extraKeys, Exception exception, string message, params object[] args) { LoggerInstance.LogDebug(extraKeys, exception, message, args); } @@ -806,7 +806,7 @@ public static void LogDebug(T extraKeys, Exception exception, string message, /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogDebug(extraKeys, "Processing request from {Address}", address) - public static void LogDebug(T extraKeys, string message, params object[] args) where T : class + public static void LogDebug(T extraKeys, string message, params object[] args) { LoggerInstance.LogDebug(extraKeys, message, args); } @@ -824,7 +824,7 @@ public static void LogDebug(T extraKeys, string message, params object[] args /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogTrace(extraKeys, 0, exception, "Error while processing request from {Address}", address) - public static void LogTrace(T extraKeys, EventId eventId, Exception exception, string message, params object[] args) where T : class + public static void LogTrace(T extraKeys, EventId eventId, Exception exception, string message, params object[] args) { LoggerInstance.LogTrace(extraKeys, eventId, exception, message, args); } @@ -837,7 +837,7 @@ public static void LogTrace(T extraKeys, EventId eventId, Exception exception /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogTrace(extraKeys, 0, "Processing request from {Address}", address) - public static void LogTrace(T extraKeys, EventId eventId, string message, params object[] args) where T : class + public static void LogTrace(T extraKeys, EventId eventId, string message, params object[] args) { LoggerInstance.LogTrace(extraKeys, eventId, message, args); } @@ -850,7 +850,7 @@ public static void LogTrace(T extraKeys, EventId eventId, string message, par /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogTrace(extraKeys, exception, "Error while processing request from {Address}", address) - public static void LogTrace(T extraKeys, Exception exception, string message, params object[] args) where T : class + public static void LogTrace(T extraKeys, Exception exception, string message, params object[] args) { LoggerInstance.LogTrace(extraKeys, exception, message, args); } @@ -862,7 +862,7 @@ public static void LogTrace(T extraKeys, Exception exception, string message, /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogTrace(extraKeys, "Processing request from {Address}", address) - public static void LogTrace(T extraKeys, string message, params object[] args) where T : class + public static void LogTrace(T extraKeys, string message, params object[] args) { LoggerInstance.LogTrace(extraKeys, message, args); } @@ -880,7 +880,7 @@ public static void LogTrace(T extraKeys, string message, params object[] args /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogInformation(extraKeys, 0, exception, "Error while processing request from {Address}", address) - public static void LogInformation(T extraKeys, EventId eventId, Exception exception, string message, params object[] args) where T : class + public static void LogInformation(T extraKeys, EventId eventId, Exception exception, string message, params object[] args) { LoggerInstance.LogInformation(extraKeys, eventId, exception, message, args); } @@ -893,7 +893,7 @@ public static void LogInformation(T extraKeys, EventId eventId, Exception exc /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogInformation(extraKeys, 0, "Processing request from {Address}", address) - public static void LogInformation(T extraKeys, EventId eventId, string message, params object[] args) where T : class + public static void LogInformation(T extraKeys, EventId eventId, string message, params object[] args) { LoggerInstance.LogInformation(extraKeys, eventId, message, args); } @@ -906,7 +906,7 @@ public static void LogInformation(T extraKeys, EventId eventId, string messag /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogInformation(extraKeys, exception, "Error while processing request from {Address}", address) - public static void LogInformation(T extraKeys, Exception exception, string message, params object[] args) where T : class + public static void LogInformation(T extraKeys, Exception exception, string message, params object[] args) { LoggerInstance.LogInformation(extraKeys, exception, message, args); } @@ -918,7 +918,7 @@ public static void LogInformation(T extraKeys, Exception exception, string me /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogInformation(extraKeys, "Processing request from {Address}", address) - public static void LogInformation(T extraKeys, string message, params object[] args) where T : class + public static void LogInformation(T extraKeys, string message, params object[] args) { LoggerInstance.LogInformation(extraKeys, message, args); } @@ -936,7 +936,7 @@ public static void LogInformation(T extraKeys, string message, params object[ /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogWarning(extraKeys, 0, exception, "Error while processing request from {Address}", address) - public static void LogWarning(T extraKeys, EventId eventId, Exception exception, string message, params object[] args) where T : class + public static void LogWarning(T extraKeys, EventId eventId, Exception exception, string message, params object[] args) { LoggerInstance.LogWarning(extraKeys, eventId, exception, message, args); } @@ -949,7 +949,7 @@ public static void LogWarning(T extraKeys, EventId eventId, Exception excepti /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogWarning(extraKeys, 0, "Processing request from {Address}", address) - public static void LogWarning(T extraKeys, EventId eventId, string message, params object[] args) where T : class + public static void LogWarning(T extraKeys, EventId eventId, string message, params object[] args) { LoggerInstance.LogWarning(extraKeys, eventId, message, args); } @@ -962,7 +962,7 @@ public static void LogWarning(T extraKeys, EventId eventId, string message, p /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogWarning(extraKeys, exception, "Error while processing request from {Address}", address) - public static void LogWarning(T extraKeys, Exception exception, string message, params object[] args) where T : class + public static void LogWarning(T extraKeys, Exception exception, string message, params object[] args) { LoggerInstance.LogWarning(extraKeys, exception, message, args); } @@ -974,7 +974,7 @@ public static void LogWarning(T extraKeys, Exception exception, string messag /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogWarning(extraKeys, "Processing request from {Address}", address) - public static void LogWarning(T extraKeys, string message, params object[] args) where T : class + public static void LogWarning(T extraKeys, string message, params object[] args) { LoggerInstance.LogWarning(extraKeys, message, args); } @@ -992,7 +992,7 @@ public static void LogWarning(T extraKeys, string message, params object[] ar /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogError(extraKeys, 0, exception, "Error while processing request from {Address}", address) - public static void LogError(T extraKeys, EventId eventId, Exception exception, string message, params object[] args) where T : class + public static void LogError(T extraKeys, EventId eventId, Exception exception, string message, params object[] args) { LoggerInstance.LogError(extraKeys, eventId, exception, message, args); } @@ -1005,7 +1005,7 @@ public static void LogError(T extraKeys, EventId eventId, Exception exception /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogError(extraKeys, 0, "Processing request from {Address}", address) - public static void LogError(T extraKeys, EventId eventId, string message, params object[] args) where T : class + public static void LogError(T extraKeys, EventId eventId, string message, params object[] args) { LoggerInstance.LogError(extraKeys, eventId, message, args); } @@ -1018,7 +1018,7 @@ public static void LogError(T extraKeys, EventId eventId, string message, par /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogError(extraKeys, exception, "Error while processing request from {Address}", address) - public static void LogError(T extraKeys, Exception exception, string message, params object[] args) where T : class + public static void LogError(T extraKeys, Exception exception, string message, params object[] args) { LoggerInstance.LogError(extraKeys, exception, message, args); } @@ -1030,7 +1030,7 @@ public static void LogError(T extraKeys, Exception exception, string message, /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogError(extraKeys, "Processing request from {Address}", address) - public static void LogError(T extraKeys, string message, params object[] args) where T : class + public static void LogError(T extraKeys, string message, params object[] args) { LoggerInstance.LogError(extraKeys, message, args); } @@ -1048,7 +1048,7 @@ public static void LogError(T extraKeys, string message, params object[] args /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogCritical(extraKeys, 0, exception, "Error while processing request from {Address}", address) - public static void LogCritical(T extraKeys, EventId eventId, Exception exception, string message, params object[] args) where T : class + public static void LogCritical(T extraKeys, EventId eventId, Exception exception, string message, params object[] args) { LoggerInstance.LogCritical(extraKeys, eventId, exception, message, args); } @@ -1061,7 +1061,7 @@ public static void LogCritical(T extraKeys, EventId eventId, Exception except /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogCritical(extraKeys, 0, "Processing request from {Address}", address) - public static void LogCritical(T extraKeys, EventId eventId, string message, params object[] args) where T : class + public static void LogCritical(T extraKeys, EventId eventId, string message, params object[] args) { LoggerInstance.LogCritical(extraKeys, eventId, message, args); } @@ -1074,7 +1074,7 @@ public static void LogCritical(T extraKeys, EventId eventId, string message, /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogCritical(extraKeys, exception, "Error while processing request from {Address}", address) - public static void LogCritical(T extraKeys, Exception exception, string message, params object[] args) where T : class + public static void LogCritical(T extraKeys, Exception exception, string message, params object[] args) { LoggerInstance.LogCritical(extraKeys, exception, message, args); } @@ -1086,7 +1086,7 @@ public static void LogCritical(T extraKeys, Exception exception, string messa /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogCritical(extraKeys, "Processing request from {Address}", address) - public static void LogCritical(T extraKeys, string message, params object[] args) where T : class + public static void LogCritical(T extraKeys, string message, params object[] args) { LoggerInstance.LogCritical(extraKeys, message, args); } @@ -1105,7 +1105,7 @@ public static void LogCritical(T extraKeys, string message, params object[] a /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.Log(LogLevel.Information, extraKeys, 0, exception, "Error while processing request from {Address}", address) - public static void Log(LogLevel logLevel, T extraKeys, EventId eventId, Exception exception, string message, params object[] args) where T : class + public static void Log(LogLevel logLevel, T extraKeys, EventId eventId, Exception exception, string message, params object[] args) { LoggerInstance.Log(logLevel, extraKeys, eventId, exception, message, args); } @@ -1119,7 +1119,7 @@ public static void Log(LogLevel logLevel, T extraKeys, EventId eventId, Excep /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.Log(LogLevel.Information, extraKeys, 0, "Processing request from {Address}", address) - public static void Log(LogLevel logLevel, T extraKeys, EventId eventId, string message, params object[] args) where T : class + public static void Log(LogLevel logLevel, T extraKeys, EventId eventId, string message, params object[] args) { LoggerInstance.Log(logLevel, extraKeys, eventId, message, args); } @@ -1133,7 +1133,7 @@ public static void Log(LogLevel logLevel, T extraKeys, EventId eventId, strin /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.Log(LogLevel.Information, extraKeys, exception, "Error while processing request from {Address}", address) - public static void Log(LogLevel logLevel, T extraKeys, Exception exception, string message, params object[] args) where T : class + public static void Log(LogLevel logLevel, T extraKeys, Exception exception, string message, params object[] args) { LoggerInstance.Log(logLevel, extraKeys, exception, message, args); } @@ -1146,7 +1146,7 @@ public static void Log(LogLevel logLevel, T extraKeys, Exception exception, s /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.Log(LogLevel.Information, extraKeys, "Processing request from {Address}", address) - public static void Log(LogLevel logLevel, T extraKeys, string message, params object[] args) where T : class + public static void Log(LogLevel logLevel, T extraKeys, string message, params object[] args) { LoggerInstance.Log(logLevel, extraKeys, message, args); } diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/LoggerExtensions.cs b/libraries/src/AWS.Lambda.Powertools.Logging/LoggerExtensions.cs index 8c8dd311..929f3ffb 100644 --- a/libraries/src/AWS.Lambda.Powertools.Logging/LoggerExtensions.cs +++ b/libraries/src/AWS.Lambda.Powertools.Logging/LoggerExtensions.cs @@ -16,6 +16,7 @@ using System; using AWS.Lambda.Powertools.Logging.Internal; using Microsoft.Extensions.Logging; +using LoggerExt = Microsoft.Extensions.Logging.LoggerExtensions; namespace AWS.Lambda.Powertools.Logging; @@ -199,7 +200,7 @@ public static void Log(this ILogger logger, LogLevel logLevel, Exception excepti /// An object array that contains zero or more objects to format. /// logger.LogDebug(extraKeys, 0, exception, "Error while processing request from {Address}", address) public static void LogDebug(this ILogger logger, T extraKeys, EventId eventId, Exception exception, - string message, params object[] args) where T : class + string message, params object[] args) { Log(logger, LogLevel.Debug, extraKeys, eventId, exception, message, args); } @@ -214,7 +215,7 @@ public static void LogDebug(this ILogger logger, T extraKeys, EventId eventId /// An object array that contains zero or more objects to format. /// logger.LogDebug(extraKeys, 0, "Processing request from {Address}", address) public static void LogDebug(this ILogger logger, T extraKeys, EventId eventId, string message, - params object[] args) where T : class + params object[] args) { Log(logger, LogLevel.Debug, extraKeys, eventId, message, args); } @@ -229,7 +230,7 @@ public static void LogDebug(this ILogger logger, T extraKeys, EventId eventId /// An object array that contains zero or more objects to format. /// logger.LogDebug(extraKeys, exception, "Error while processing request from {Address}", address) public static void LogDebug(this ILogger logger, T extraKeys, Exception exception, string message, - params object[] args) where T : class + params object[] args) { Log(logger, LogLevel.Debug, extraKeys, exception, message, args); } @@ -242,7 +243,7 @@ public static void LogDebug(this ILogger logger, T extraKeys, Exception excep /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogDebug(extraKeys, "Processing request from {Address}", address) - public static void LogDebug(this ILogger logger, T extraKeys, string message, params object[] args) where T : class + public static void LogDebug(this ILogger logger, T extraKeys, string message, params object[] args) { Log(logger, LogLevel.Debug, extraKeys, message, args); } @@ -262,7 +263,7 @@ public static void LogDebug(this ILogger logger, T extraKeys, string message, /// An object array that contains zero or more objects to format. /// logger.LogTrace(extraKeys, 0, exception, "Error while processing request from {Address}", address) public static void LogTrace(this ILogger logger, T extraKeys, EventId eventId, Exception exception, - string message, params object[] args) where T : class + string message, params object[] args) { Log(logger, LogLevel.Trace, extraKeys, eventId, exception, message, args); } @@ -277,7 +278,7 @@ public static void LogTrace(this ILogger logger, T extraKeys, EventId eventId /// An object array that contains zero or more objects to format. /// logger.LogTrace(extraKeys, 0, "Processing request from {Address}", address) public static void LogTrace(this ILogger logger, T extraKeys, EventId eventId, string message, - params object[] args) where T : class + params object[] args) { Log(logger, LogLevel.Trace, extraKeys, eventId, message, args); } @@ -292,7 +293,7 @@ public static void LogTrace(this ILogger logger, T extraKeys, EventId eventId /// An object array that contains zero or more objects to format. /// logger.LogTrace(extraKeys, exception, "Error while processing request from {Address}", address) public static void LogTrace(this ILogger logger, T extraKeys, Exception exception, string message, - params object[] args) where T : class + params object[] args) { Log(logger, LogLevel.Trace, extraKeys, exception, message, args); } @@ -305,7 +306,7 @@ public static void LogTrace(this ILogger logger, T extraKeys, Exception excep /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogTrace(extraKeys, "Processing request from {Address}", address) - public static void LogTrace(this ILogger logger, T extraKeys, string message, params object[] args) where T : class + public static void LogTrace(this ILogger logger, T extraKeys, string message, params object[] args) { Log(logger, LogLevel.Trace, extraKeys, message, args); } @@ -325,7 +326,7 @@ public static void LogTrace(this ILogger logger, T extraKeys, string message, /// An object array that contains zero or more objects to format. /// logger.LogInformation(extraKeys, 0, exception, "Error while processing request from {Address}", address) public static void LogInformation(this ILogger logger, T extraKeys, EventId eventId, Exception exception, - string message, params object[] args) where T : class + string message, params object[] args) { Log(logger, LogLevel.Information, extraKeys, eventId, exception, message, args); } @@ -340,7 +341,7 @@ public static void LogInformation(this ILogger logger, T extraKeys, EventId e /// An object array that contains zero or more objects to format. /// logger.LogInformation(extraKeys, 0, "Processing request from {Address}", address) public static void LogInformation(this ILogger logger, T extraKeys, EventId eventId, string message, - params object[] args) where T : class + params object[] args) { Log(logger, LogLevel.Information, extraKeys, eventId, message, args); } @@ -355,7 +356,7 @@ public static void LogInformation(this ILogger logger, T extraKeys, EventId e /// An object array that contains zero or more objects to format. /// logger.LogInformation(extraKeys, exception, "Error while processing request from {Address}", address) public static void LogInformation(this ILogger logger, T extraKeys, Exception exception, string message, - params object[] args) where T : class + params object[] args) { Log(logger, LogLevel.Information, extraKeys, exception, message, args); } @@ -368,7 +369,7 @@ public static void LogInformation(this ILogger logger, T extraKeys, Exception /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogInformation(extraKeys, "Processing request from {Address}", address) - public static void LogInformation(this ILogger logger, T extraKeys, string message, params object[] args) where T : class + public static void LogInformation(this ILogger logger, T extraKeys, string message, params object[] args) { Log(logger, LogLevel.Information, extraKeys, message, args); } @@ -388,7 +389,7 @@ public static void LogInformation(this ILogger logger, T extraKeys, string me /// An object array that contains zero or more objects to format. /// logger.LogWarning(extraKeys, 0, exception, "Error while processing request from {Address}", address) public static void LogWarning(this ILogger logger, T extraKeys, EventId eventId, Exception exception, - string message, params object[] args) where T : class + string message, params object[] args) { Log(logger, LogLevel.Warning, extraKeys, eventId, exception, message, args); } @@ -403,7 +404,7 @@ public static void LogWarning(this ILogger logger, T extraKeys, EventId event /// An object array that contains zero or more objects to format. /// logger.LogWarning(extraKeys, 0, "Processing request from {Address}", address) public static void LogWarning(this ILogger logger, T extraKeys, EventId eventId, string message, - params object[] args) where T : class + params object[] args) { Log(logger, LogLevel.Warning, extraKeys, eventId, message, args); } @@ -418,7 +419,7 @@ public static void LogWarning(this ILogger logger, T extraKeys, EventId event /// An object array that contains zero or more objects to format. /// logger.LogWarning(extraKeys, exception, "Error while processing request from {Address}", address) public static void LogWarning(this ILogger logger, T extraKeys, Exception exception, string message, - params object[] args) where T : class + params object[] args) { Log(logger, LogLevel.Warning, extraKeys, exception, message, args); } @@ -431,7 +432,7 @@ public static void LogWarning(this ILogger logger, T extraKeys, Exception exc /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogWarning(extraKeys, "Processing request from {Address}", address) - public static void LogWarning(this ILogger logger, T extraKeys, string message, params object[] args) where T : class + public static void LogWarning(this ILogger logger, T extraKeys, string message, params object[] args) { Log(logger, LogLevel.Warning, extraKeys, message, args); } @@ -451,7 +452,7 @@ public static void LogWarning(this ILogger logger, T extraKeys, string messag /// An object array that contains zero or more objects to format. /// logger.LogError(extraKeys, 0, exception, "Error while processing request from {Address}", address) public static void LogError(this ILogger logger, T extraKeys, EventId eventId, Exception exception, - string message, params object[] args) where T : class + string message, params object[] args) { Log(logger, LogLevel.Error, extraKeys, eventId, exception, message, args); } @@ -466,7 +467,7 @@ public static void LogError(this ILogger logger, T extraKeys, EventId eventId /// An object array that contains zero or more objects to format. /// logger.LogError(extraKeys, 0, "Processing request from {Address}", address) public static void LogError(this ILogger logger, T extraKeys, EventId eventId, string message, - params object[] args) where T : class + params object[] args) { Log(logger, LogLevel.Error, extraKeys, eventId, message, args); } @@ -481,7 +482,7 @@ public static void LogError(this ILogger logger, T extraKeys, EventId eventId /// An object array that contains zero or more objects to format. /// logger.LogError(extraKeys, exception, "Error while processing request from {Address}", address) public static void LogError(this ILogger logger, T extraKeys, Exception exception, string message, - params object[] args) where T : class + params object[] args) { Log(logger, LogLevel.Error, extraKeys, exception, message, args); } @@ -494,7 +495,7 @@ public static void LogError(this ILogger logger, T extraKeys, Exception excep /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogError(extraKeys, "Processing request from {Address}", address) - public static void LogError(this ILogger logger, T extraKeys, string message, params object[] args) where T : class + public static void LogError(this ILogger logger, T extraKeys, string message, params object[] args) { Log(logger, LogLevel.Error, extraKeys, message, args); } @@ -514,7 +515,7 @@ public static void LogError(this ILogger logger, T extraKeys, string message, /// An object array that contains zero or more objects to format. /// logger.LogCritical(extraKeys, 0, exception, "Error while processing request from {Address}", address) public static void LogCritical(this ILogger logger, T extraKeys, EventId eventId, Exception exception, - string message, params object[] args) where T : class + string message, params object[] args) { Log(logger, LogLevel.Critical, extraKeys, eventId, exception, message, args); } @@ -529,7 +530,7 @@ public static void LogCritical(this ILogger logger, T extraKeys, EventId even /// An object array that contains zero or more objects to format. /// logger.LogCritical(extraKeys, 0, "Processing request from {Address}", address) public static void LogCritical(this ILogger logger, T extraKeys, EventId eventId, string message, - params object[] args) where T : class + params object[] args) { Log(logger, LogLevel.Critical, extraKeys, eventId, message, args); } @@ -544,7 +545,7 @@ public static void LogCritical(this ILogger logger, T extraKeys, EventId even /// An object array that contains zero or more objects to format. /// logger.LogCritical(extraKeys, exception, "Error while processing request from {Address}", address) public static void LogCritical(this ILogger logger, T extraKeys, Exception exception, string message, - params object[] args) where T : class + params object[] args) { Log(logger, LogLevel.Critical, extraKeys, exception, message, args); } @@ -557,7 +558,7 @@ public static void LogCritical(this ILogger logger, T extraKeys, Exception ex /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogCritical(extraKeys, "Processing request from {Address}", address) - public static void LogCritical(this ILogger logger, T extraKeys, string message, params object[] args) where T : class + public static void LogCritical(this ILogger logger, T extraKeys, string message, params object[] args) { Log(logger, LogLevel.Critical, extraKeys, message, args); } @@ -565,7 +566,7 @@ public static void LogCritical(this ILogger logger, T extraKeys, string messa #endregion #region Log - + /// /// Formats and writes a log message at the specified log level. /// @@ -578,15 +579,17 @@ public static void LogCritical(this ILogger logger, T extraKeys, string messa /// An object array that contains zero or more objects to format. /// logger.Log(LogLevel.Information, extraKeys, 0, exception, "Error while processing request from {Address}", address) public static void Log(this ILogger logger, LogLevel logLevel, T extraKeys, EventId eventId, Exception exception, - string message, params object[] args) where T : class + string message, params object[] args) { if (extraKeys is Exception ex && exception is null) - logger.Log(logLevel, eventId, ex, message, args); + LoggerExt.Log(logger, logLevel, eventId, ex, message, args); + else if (extraKeys is EventId evid && evid == 0) + LoggerExt.Log(logger, logLevel, evid, exception, message, args); else if (extraKeys is not null) using (logger.BeginScope(extraKeys)) - logger.Log(logLevel, eventId, exception, message, args); + LoggerExt.Log(logger, logLevel, eventId, exception, message, args); else - logger.Log(logLevel, eventId, exception, message, args); + LoggerExt.Log(logger, logLevel, eventId, exception, message, args); } /// @@ -600,7 +603,7 @@ public static void Log(this ILogger logger, LogLevel logLevel, T extraKeys, E /// An object array that contains zero or more objects to format. /// logger.Log(LogLevel.Information, extraKeys, 0, "Processing request from {Address}", address) public static void Log(this ILogger logger, LogLevel logLevel, T extraKeys, EventId eventId, string message, - params object[] args) where T : class + params object[] args) { Log(logger, logLevel, extraKeys, eventId, null, message, args); } @@ -616,7 +619,7 @@ public static void Log(this ILogger logger, LogLevel logLevel, T extraKeys, E /// An object array that contains zero or more objects to format. /// logger.Log(LogLevel.Information, extraKeys, exception, "Error while processing request from {Address}", address) public static void Log(this ILogger logger, LogLevel logLevel, T extraKeys, Exception exception, string message, - params object[] args) where T : class + params object[] args) { Log(logger, logLevel, extraKeys, 0, exception, message, args); } @@ -630,7 +633,7 @@ public static void Log(this ILogger logger, LogLevel logLevel, T extraKeys, E /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.Log(LogLevel.Information, extraKeys, "Processing request from {Address}", address) - public static void Log(this ILogger logger, LogLevel logLevel, T extraKeys, string message, params object[] args) where T : class + public static void Log(this ILogger logger, LogLevel logLevel, T extraKeys, string message, params object[] args) { Log(logger, logLevel, extraKeys, 0, null, message, args); } diff --git a/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerTest.cs b/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerTest.cs index d962ba2f..585414ae 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerTest.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerTest.cs @@ -652,7 +652,41 @@ public void Log_NoOutputCaseSet_OutputDefaultsToSnakeCaseLog() } [Fact] - public void BeginScope_WhenScopeIsObject_ExtractScopeKeys() + public void BeginScope_WhenScopeIsString_AddExtraKey() + { + // Arrange + var loggerName = Guid.NewGuid().ToString(); + var service = Guid.NewGuid().ToString(); + var logLevel = LogLevel.Information; + + var configurations = new Mock(); + configurations.Setup(c => c.Service).Returns(service); + configurations.Setup(c => c.LogLevel).Returns(logLevel.ToString); + var systemWrapper = new Mock(); + + var logger = new PowertoolsLogger(loggerName,configurations.Object, systemWrapper.Object, () => + new LoggerConfiguration + { + Service = service, + MinimumLevel = logLevel + }); + + var keyName = LoggingConstants.KeyExtra; + var scopeKeys = Guid.NewGuid().ToString(); + + using (var loggerScope = logger.BeginScope(scopeKeys) as PowertoolsLoggerScope) + { + Assert.NotNull(loggerScope); + Assert.NotNull(loggerScope.ExtraKeys); + Assert.True(loggerScope.ExtraKeys.Count == 1); + Assert.True(loggerScope.ExtraKeys.ContainsKey(keyName)); + Assert.True((string)loggerScope.ExtraKeys[keyName] == scopeKeys); + } + Assert.Null(logger.CurrentScope?.ExtraKeys); + } + + [Fact] + public void BeginScope_WhenScopeIsObject_AddExtraKey() { // Arrange var loggerName = Guid.NewGuid().ToString(); @@ -671,6 +705,7 @@ public void BeginScope_WhenScopeIsObject_ExtractScopeKeys() MinimumLevel = logLevel }); + var keyName = LoggingConstants.KeyExtra; var scopeKeys = new { PropOne = "Value 1", @@ -681,15 +716,52 @@ public void BeginScope_WhenScopeIsObject_ExtractScopeKeys() { Assert.NotNull(loggerScope); Assert.NotNull(loggerScope.ExtraKeys); - Assert.True(loggerScope.ExtraKeys.Count == 2); - Assert.True(loggerScope.ExtraKeys.ContainsKey("PropOne")); - Assert.True((string)loggerScope.ExtraKeys["PropOne"] == scopeKeys.PropOne); - Assert.True(loggerScope.ExtraKeys.ContainsKey("PropTwo")); - Assert.True((string)loggerScope.ExtraKeys["PropTwo"] == scopeKeys.PropTwo); + Assert.True(loggerScope.ExtraKeys.Count == 1); + Assert.True(loggerScope.ExtraKeys.ContainsKey(keyName)); + Assert.True(loggerScope.ExtraKeys[keyName] == scopeKeys); } Assert.Null(logger.CurrentScope?.ExtraKeys); } + [Fact] + public void BeginScope_WhenScopeIsTuple_ExtractScopeKeys() + { + // Arrange + var loggerName = Guid.NewGuid().ToString(); + var service = Guid.NewGuid().ToString(); + var logLevel = LogLevel.Information; + + var configurations = new Mock(); + configurations.Setup(c => c.Service).Returns(service); + configurations.Setup(c => c.LogLevel).Returns(logLevel.ToString); + var systemWrapper = new Mock(); + + var logger = new PowertoolsLogger(loggerName,configurations.Object, systemWrapper.Object, () => + new LoggerConfiguration + { + Service = service, + MinimumLevel = logLevel + }); + + var keyName = Guid.NewGuid().ToString(); + var scopeKeys = new + { + PropOne = "Value 1", + PropTwo = "Value 2" + }; + + using (var loggerScope = logger.BeginScope((keyName, scopeKeys)) as PowertoolsLoggerScope) + { + Assert.NotNull(loggerScope); + Assert.NotNull(loggerScope.ExtraKeys); + Assert.True(loggerScope.ExtraKeys.Count == 1); + Assert.True(loggerScope.ExtraKeys.ContainsKey(keyName)); + Assert.True(loggerScope.ExtraKeys[keyName] == scopeKeys); + } + + Assert.Null(logger.CurrentScope?.ExtraKeys); + } + [Fact] public void BeginScope_WhenScopeIsObjectDictionary_ExtractScopeKeys() { From ff883e6c76075cd594f1219317bb206e96405b40 Mon Sep 17 00:00:00 2001 From: Amir Khairalomoum Date: Tue, 18 Apr 2023 19:41:45 +0100 Subject: [PATCH 2/4] change extra keys logger to accept any type and add Extra when keyname is not specified --- .../Internal/PowertoolsLogger.cs | 43 ++- .../PowertoolsLoggerTest.cs | 277 +++++++++++++++++- 2 files changed, 313 insertions(+), 7 deletions(-) diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsLogger.cs b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsLogger.cs index ba1d0694..dff557e2 100644 --- a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsLogger.cs +++ b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsLogger.cs @@ -140,10 +140,10 @@ internal void EndScope() private static Dictionary GetScopeKeys(TState state) { var keys = new Dictionary(); - - if (state is null) + + if (state is null) return keys; - + switch (state) { case IEnumerable> pairs: @@ -153,6 +153,7 @@ private static Dictionary GetScopeKeys(TState state) if (!string.IsNullOrWhiteSpace(key)) keys.TryAdd(key, value); } + break; } case IEnumerable> pairs: @@ -162,6 +163,7 @@ private static Dictionary GetScopeKeys(TState state) if (!string.IsNullOrWhiteSpace(key)) keys.TryAdd(key, value); } + break; } case KeyValuePair(var key, var value): @@ -186,14 +188,45 @@ private static Dictionary GetScopeKeys(TState state) } default: { - keys.TryAdd(LoggingConstants.KeyExtra, state); + keys.TryAdd(GetScopeKey(state), state); break; } } - + return keys; } + /// + /// Extract provided scope key + /// + /// The type of the t state. + /// The state. + /// Key for the provided scope key + private static string GetScopeKey(TState state) + { + if (state is null or string) + return LoggingConstants.KeyExtra; + + var type = state.GetType(); + if (type.IsEnum || + type.IsArray || + type.IsPrimitive || + string.IsNullOrWhiteSpace(type.Namespace)) + return LoggingConstants.KeyExtra; + + var typeName = type.Name; + if (type.IsGenericType) + typeName = type.Name.Split('`').First(); + + if (typeName.Contains('[') || + typeName.Contains(']') || + typeName.Contains('<') || + typeName.Contains('>')) + return LoggingConstants.KeyExtra; + + return typeName; + } + /// /// Determines whether the specified log level is enabled. /// diff --git a/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerTest.cs b/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerTest.cs index 585414ae..03ab0312 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerTest.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerTest.cs @@ -18,6 +18,7 @@ using System.Globalization; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using AWS.Lambda.Powertools.Common; using AWS.Lambda.Powertools.Logging.Internal; @@ -651,6 +652,40 @@ public void Log_NoOutputCaseSet_OutputDefaultsToSnakeCaseLog() ), Times.Once); } + [Fact] + public void BeginScope_WhenScopeIsPrimitive_AddExtraKey() + { + // Arrange + var loggerName = Guid.NewGuid().ToString(); + var service = Guid.NewGuid().ToString(); + var logLevel = LogLevel.Information; + + var configurations = new Mock(); + configurations.Setup(c => c.Service).Returns(service); + configurations.Setup(c => c.LogLevel).Returns(logLevel.ToString); + var systemWrapper = new Mock(); + + var logger = new PowertoolsLogger(loggerName,configurations.Object, systemWrapper.Object, () => + new LoggerConfiguration + { + Service = service, + MinimumLevel = logLevel + }); + + var keyName = LoggingConstants.KeyExtra; + var scopeKeys = 20; + + using (var loggerScope = logger.BeginScope(scopeKeys) as PowertoolsLoggerScope) + { + Assert.NotNull(loggerScope); + Assert.NotNull(loggerScope.ExtraKeys); + Assert.True(loggerScope.ExtraKeys.Count == 1); + Assert.True(loggerScope.ExtraKeys.ContainsKey(keyName)); + Assert.True((int)loggerScope.ExtraKeys[keyName] == scopeKeys); + } + Assert.Null(logger.CurrentScope?.ExtraKeys); + } + [Fact] public void BeginScope_WhenScopeIsString_AddExtraKey() { @@ -685,8 +720,8 @@ public void BeginScope_WhenScopeIsString_AddExtraKey() Assert.Null(logger.CurrentScope?.ExtraKeys); } - [Fact] - public void BeginScope_WhenScopeIsObject_AddExtraKey() + [Fact] + public void BeginScope_WhenScopeIsAnonymousClass_AddExtraKey() { // Arrange var loggerName = Guid.NewGuid().ToString(); @@ -723,6 +758,244 @@ public void BeginScope_WhenScopeIsObject_AddExtraKey() Assert.Null(logger.CurrentScope?.ExtraKeys); } + [Fact] + public void BeginScope_WhenScopeIsNonKeyValuePairTuple_AddExtraKey() + { + // Arrange + var loggerName = Guid.NewGuid().ToString(); + var service = Guid.NewGuid().ToString(); + var logLevel = LogLevel.Information; + + var configurations = new Mock(); + configurations.Setup(c => c.Service).Returns(service); + configurations.Setup(c => c.LogLevel).Returns(logLevel.ToString); + var systemWrapper = new Mock(); + + var logger = new PowertoolsLogger(loggerName,configurations.Object, systemWrapper.Object, () => + new LoggerConfiguration + { + Service = service, + MinimumLevel = logLevel + }); + + var keyName = LoggingConstants.KeyExtra; + var scopeKeys = ("Value 1", "Value 2", "Value 2"); + + using (var loggerScope = logger.BeginScope(scopeKeys) as PowertoolsLoggerScope) + { + Assert.NotNull(loggerScope); + Assert.NotNull(loggerScope.ExtraKeys); + Assert.True(loggerScope.ExtraKeys.Count == 1); + Assert.True(loggerScope.ExtraKeys.ContainsKey(keyName)); + Assert.True(((ITuple)loggerScope.ExtraKeys[keyName]).Length == ((ITuple)scopeKeys).Length); + } + Assert.Null(logger.CurrentScope?.ExtraKeys); + } + + [Fact] + public void BeginScope_WhenScopeIsArray_AddExtraKey() + { + // Arrange + var loggerName = Guid.NewGuid().ToString(); + var service = Guid.NewGuid().ToString(); + var logLevel = LogLevel.Information; + + var configurations = new Mock(); + configurations.Setup(c => c.Service).Returns(service); + configurations.Setup(c => c.LogLevel).Returns(logLevel.ToString); + var systemWrapper = new Mock(); + + var logger = new PowertoolsLogger(loggerName,configurations.Object, systemWrapper.Object, () => + new LoggerConfiguration + { + Service = service, + MinimumLevel = logLevel + }); + + var keyName = LoggingConstants.KeyExtra; + var scopeKeys = new[] { Guid.NewGuid().ToString() }; + + using (var loggerScope = logger.BeginScope(scopeKeys) as PowertoolsLoggerScope) + { + Assert.NotNull(loggerScope); + Assert.NotNull(loggerScope.ExtraKeys); + Assert.True(loggerScope.ExtraKeys.Count == 1); + Assert.True(loggerScope.ExtraKeys.ContainsKey(keyName)); + Assert.True((string[])loggerScope.ExtraKeys[keyName] == scopeKeys); + } + Assert.Null(logger.CurrentScope?.ExtraKeys); + } + + [Fact] + public void BeginScope_WhenScopeIsDynamic_AddExtraKey() + { + // Arrange + var loggerName = Guid.NewGuid().ToString(); + var service = Guid.NewGuid().ToString(); + var logLevel = LogLevel.Information; + + var configurations = new Mock(); + configurations.Setup(c => c.Service).Returns(service); + configurations.Setup(c => c.LogLevel).Returns(logLevel.ToString); + var systemWrapper = new Mock(); + + var logger = new PowertoolsLogger(loggerName,configurations.Object, systemWrapper.Object, () => + new LoggerConfiguration + { + Service = service, + MinimumLevel = logLevel + }); + + var keyName = LoggingConstants.KeyExtra; + dynamic scopeKeys = 1; + + using (var loggerScope = logger.BeginScope(scopeKeys) as PowertoolsLoggerScope) + { + Assert.NotNull(loggerScope); + Assert.NotNull(loggerScope.ExtraKeys); + Assert.True(loggerScope.ExtraKeys.Count == 1); + Assert.True(loggerScope.ExtraKeys.ContainsKey(keyName)); + Assert.True((int)loggerScope.ExtraKeys[keyName] == scopeKeys); + } + Assert.Null(logger.CurrentScope?.ExtraKeys); + } + + [Fact] + public void BeginScope_WhenScopeIsEnum_AddExtraKey() + { + // Arrange + var loggerName = Guid.NewGuid().ToString(); + var service = Guid.NewGuid().ToString(); + var logLevel = LogLevel.Information; + + var configurations = new Mock(); + configurations.Setup(c => c.Service).Returns(service); + configurations.Setup(c => c.LogLevel).Returns(logLevel.ToString); + var systemWrapper = new Mock(); + + var logger = new PowertoolsLogger(loggerName,configurations.Object, systemWrapper.Object, () => + new LoggerConfiguration + { + Service = service, + MinimumLevel = logLevel + }); + + var keyName = LoggingConstants.KeyExtra; + var scopeKeys = LoggerOutputCase.PascalCase; + + using (var loggerScope = logger.BeginScope(scopeKeys) as PowertoolsLoggerScope) + { + Assert.NotNull(loggerScope); + Assert.NotNull(loggerScope.ExtraKeys); + Assert.True(loggerScope.ExtraKeys.Count == 1); + Assert.True(loggerScope.ExtraKeys.ContainsKey(keyName)); + Assert.True((LoggerOutputCase)loggerScope.ExtraKeys[keyName] == scopeKeys); + } + Assert.Null(logger.CurrentScope?.ExtraKeys); + } + + [Fact] + public void BeginScope_WhenScopeIsStruct_ExtractExtraKeyName() + { + // Arrange + var loggerName = Guid.NewGuid().ToString(); + var service = Guid.NewGuid().ToString(); + var logLevel = LogLevel.Information; + + var configurations = new Mock(); + configurations.Setup(c => c.Service).Returns(service); + configurations.Setup(c => c.LogLevel).Returns(logLevel.ToString); + var systemWrapper = new Mock(); + + var logger = new PowertoolsLogger(loggerName,configurations.Object, systemWrapper.Object, () => + new LoggerConfiguration + { + Service = service, + MinimumLevel = logLevel + }); + + var keyName = "Point"; + var scopeKeys = new System.Drawing.Point { X = 100, Y = 150 }; + + using (var loggerScope = logger.BeginScope(scopeKeys) as PowertoolsLoggerScope) + { + Assert.NotNull(loggerScope); + Assert.NotNull(loggerScope.ExtraKeys); + Assert.True(loggerScope.ExtraKeys.Count == 1); + Assert.True(loggerScope.ExtraKeys.ContainsKey(keyName)); + Assert.True((System.Drawing.Point)loggerScope.ExtraKeys[keyName] == scopeKeys); + } + Assert.Null(logger.CurrentScope?.ExtraKeys); + } + + [Fact] + public void BeginScope_WhenScopeIsTypedClass_ExtractExtraKeyName() + { + // Arrange + var loggerName = Guid.NewGuid().ToString(); + var service = Guid.NewGuid().ToString(); + var logLevel = LogLevel.Information; + + var configurations = new Mock(); + configurations.Setup(c => c.Service).Returns(service); + configurations.Setup(c => c.LogLevel).Returns(logLevel.ToString); + var systemWrapper = new Mock(); + + var logger = new PowertoolsLogger(loggerName,configurations.Object, systemWrapper.Object, () => + new LoggerConfiguration + { + Service = service, + MinimumLevel = logLevel + }); + + var keyName = "LoggerConfiguration"; + var scopeKeys = new LoggerConfiguration(); + + using (var loggerScope = logger.BeginScope(scopeKeys) as PowertoolsLoggerScope) + { + Assert.NotNull(loggerScope); + Assert.NotNull(loggerScope.ExtraKeys); + Assert.True(loggerScope.ExtraKeys.Count == 1); + Assert.True(loggerScope.ExtraKeys.ContainsKey(keyName)); + Assert.True((LoggerConfiguration)loggerScope.ExtraKeys[keyName] == scopeKeys); + } + Assert.Null(logger.CurrentScope?.ExtraKeys); + } + + [Fact] + public void BeginScope_WhenScopeIsGenericTypedClass_ExtractExtraKeyName() + { + // Arrange + var loggerName = Guid.NewGuid().ToString(); + var service = Guid.NewGuid().ToString(); + var logLevel = LogLevel.Information; + + var configurations = new Mock(); + configurations.Setup(c => c.Service).Returns(service); + configurations.Setup(c => c.LogLevel).Returns(logLevel.ToString); + var systemWrapper = new Mock(); + + var logger = new PowertoolsLogger(loggerName,configurations.Object, systemWrapper.Object, () => + new LoggerConfiguration + { + Service = service, + MinimumLevel = logLevel + }); + + var keyName = "List"; + var scopeKeys = new List { Guid.NewGuid().ToString() }; + + using (var loggerScope = logger.BeginScope(scopeKeys) as PowertoolsLoggerScope) + { + Assert.NotNull(loggerScope); + Assert.NotNull(loggerScope.ExtraKeys); + Assert.True(loggerScope.ExtraKeys.Count == 1); + Assert.True(loggerScope.ExtraKeys.ContainsKey(keyName)); + Assert.True((List)loggerScope.ExtraKeys[keyName] == scopeKeys); + } + Assert.Null(logger.CurrentScope?.ExtraKeys); + } + [Fact] public void BeginScope_WhenScopeIsTuple_ExtractScopeKeys() { From 48f806cda38b8f7130aee4d3d7471338fbd73728 Mon Sep 17 00:00:00 2001 From: Amir Khairalomoum Date: Wed, 19 Apr 2023 09:32:05 +0100 Subject: [PATCH 3/4] update extra key documentation --- docs/core/logging.md | 48 +++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/docs/core/logging.md b/docs/core/logging.md index 7173af78..2136ba18 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -311,7 +311,7 @@ It accepts any dictionary, and all keyword arguments will be added as part of th === "Function.cs" - ```c# hl_lines="15 18 27" + ```c# hl_lines="14 17 21 29" /** * Handler for requests to Lambda function. */ @@ -323,28 +323,35 @@ It accepts any dictionary, and all keyword arguments will be added as part of th { var requestContextRequestId = apigwProxyEvent.RequestContext.RequestId; + // Pass an object for logging additional data var lookupInfo = new { LookupId = requestContextRequestId }; - - // Simply pass an object for logging additional data - Logger.LogInformation(lookupInfo, "This is a log with additional data"); + Logger.LogInformation(lookupInfo, "This is a log with anonymous type object additional data"); // Specify the key name for extra additional data - Logger.LogInformation(("LookupInfo", lookupInfo), "This is a log with an extra variable"); + Logger.LogInformation(("LookupInfo", lookupInfo), "This is a log specifying the key name for additional data"); + + // Pass a typed object for logging additional data + var vLookupInfo = new LookupInfo { LookupId = requestContextRequestId }; + Logger.LogInformation(vLookupInfo, "This is a log with typed object additional data"); + // Pass multiple extra key/value pairs var extraKeys = new Dictionary() { { "LookupInfo", lookupInfo }, { "CorrelationIds", new { MyCorrelationId = "correlation_id_value" } } }; - - // Specify the key name for extra additional data - Logger.LogInformation(extraKeys, "This is a log with multiple extra variables"); + Logger.LogInformation(extraKeys, "This is a log with multiple extra key/value pairs"); + ... + } + public class LookupInfo + { + public string? LookupId { get; set; } } ``` === "Example CloudWatch Logs excerpt" - ```json hl_lines="4-6 21-23 38-43" + ```json hl_lines="4-6 21-23 38-40 55-60" { "cold_start": true, "xray_trace_id": "1-61b7add4-66532bb81441e1b060389429", @@ -360,7 +367,24 @@ It accepts any dictionary, and all keyword arguments will be added as part of th "level": "Information", "service": "lambda-example", "name": "AWS.Lambda.Powertools.Logging.Logger", - "message": "This is a log with additional data", + "message": "This is a log with anonymous type object additional data", + } + { + "cold_start": true, + "xray_trace_id": "1-61b7add4-66532bb81441e1b060389429", + "lookup_info": { + "lookup_id": "4c50eace-8b1e-43d3-92ba-0efacf5d1625" + }, + "function_name": "test", + "function_version": "$LATEST", + "function_memory_size": 128, + "function_arn": "arn:aws:lambda:eu-west-1:12345678910:function:test", + "function_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72", + "timestamp": "2021-12-13T20:32:22.5774262Z", + "level": "Information", + "service": "lambda-example", + "name": "AWS.Lambda.Powertools.Logging.Logger", + "message": "This is a log specifying the key name for additional data", } { "cold_start": true, @@ -377,7 +401,7 @@ It accepts any dictionary, and all keyword arguments will be added as part of th "level": "Information", "service": "lambda-example", "name": "AWS.Lambda.Powertools.Logging.Logger", - "message": "This is a log with an extra variable", + "message": "This is a log with typed object additional data", } { "cold_start": true, @@ -397,7 +421,7 @@ It accepts any dictionary, and all keyword arguments will be added as part of th "level": "Information", "service": "lambda-example", "name": "AWS.Lambda.Powertools.Logging.Logger", - "message": "This is a log with multiple extra variables", + "message": "This is a log with multiple extra key/value pairs", } ``` From 4d763248ed3a9d8adab154308c7cc5eafdf5588e Mon Sep 17 00:00:00 2001 From: Amir Khairalomoum Date: Wed, 19 Apr 2023 10:01:04 +0100 Subject: [PATCH 4/4] change extra key variable name in documentation --- docs/core/logging.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/core/logging.md b/docs/core/logging.md index 2136ba18..62053a0b 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -324,20 +324,20 @@ It accepts any dictionary, and all keyword arguments will be added as part of th var requestContextRequestId = apigwProxyEvent.RequestContext.RequestId; // Pass an object for logging additional data - var lookupInfo = new { LookupId = requestContextRequestId }; - Logger.LogInformation(lookupInfo, "This is a log with anonymous type object additional data"); + var lookupInfo1 = new { LookupId = requestContextRequestId }; + Logger.LogInformation(lookupInfo1, "This is a log with anonymous type object additional data"); // Specify the key name for extra additional data - Logger.LogInformation(("LookupInfo", lookupInfo), "This is a log specifying the key name for additional data"); + Logger.LogInformation(("LookupInfo", lookupInfo1), "This is a log specifying the key name for additional data"); // Pass a typed object for logging additional data - var vLookupInfo = new LookupInfo { LookupId = requestContextRequestId }; - Logger.LogInformation(vLookupInfo, "This is a log with typed object additional data"); + var lookupInfo2 = new LookupInfo { LookupId = requestContextRequestId }; + Logger.LogInformation(lookupInfo2, "This is a log with typed object additional data"); // Pass multiple extra key/value pairs var extraKeys = new Dictionary() { - { "LookupInfo", lookupInfo }, + { "LookupInfo", new { LookupId = requestContextRequestId } }, { "CorrelationIds", new { MyCorrelationId = "correlation_id_value" } } }; Logger.LogInformation(extraKeys, "This is a log with multiple extra key/value pairs");