diff --git a/docs/core/logging.md b/docs/core/logging.md index f129f22ba..b9ca2a17d 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -255,6 +255,39 @@ You can remove any additional key from entry using `Logger.RemoveKeys()`. } ``` +## Extra Keys + +Extra keys argument is available for all log levels' methods, as implemented in the standard logging library - e.g. Logger.Information, Logger.Warning. + +It accepts any dictionary, and all keyword arguments will be added as part of the root structure of the logs for that log statement. + +!!! info + Any keyword argument added using extra keys will not be persisted for subsequent messages. + +=== "Function.cs" + + ```c# hl_lines="16" + /** + * Handler for requests to Lambda function. + */ + public class Function + { + [Logging(LogEvent = true)] + public async Task FunctionHandler + (APIGatewayProxyRequest apigProxyEvent, ILambdaContext context) + { + ... + var extraKeys = new Dictionary + { + {"extraKey1", "value1"} + }; + + Logger.LogInformation(extraKeys, "Collecting payment"); + ... + } + } + ``` + ### Clearing all state Logger is commonly initialized in the global scope. Due to [Lambda Execution Context reuse](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-context.html), this means that custom keys can be persisted across invocations. If you want all custom keys to be deleted, you can use `ClearState=true` attribute on `[Logging]` attribute. diff --git a/examples/Logging/src/HelloWorld/Function.cs b/examples/Logging/src/HelloWorld/Function.cs index 4c8e9d085..4ef91a25a 100644 --- a/examples/Logging/src/HelloWorld/Function.cs +++ b/examples/Logging/src/HelloWorld/Function.cs @@ -91,12 +91,12 @@ public async Task FunctionHandler(APIGatewayProxyReques try { - Logger.LogInformation("Calling Check IP API "); + Logger.LogInformation("Calling Check IP API"); var response = await _httpClient.GetStringAsync("https://checkip.amazonaws.com/").ConfigureAwait(false); var ip = response.Replace("\n", ""); - Logger.LogInformation("API response returned {}", ip); + Logger.LogInformation($"API response returned {ip}"); return ip; } diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingAspectHandler.cs b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingAspectHandler.cs index ae7b421f6..2e649e297 100644 --- a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingAspectHandler.cs +++ b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingAspectHandler.cs @@ -58,6 +58,11 @@ internal class LoggingAspectHandler : IMethodAspectHandler /// The log level /// private readonly LogLevel? _logLevel; + + /// + /// The logger output case + /// + private readonly LoggerOutputCase? _loggerOutputCase; /// /// The Powertools configurations @@ -94,6 +99,7 @@ internal class LoggingAspectHandler : IMethodAspectHandler /// /// Service name /// The log level. + /// The logger output case. /// The sampling rate. /// if set to true [log event]. /// The correlation identifier path. @@ -104,6 +110,7 @@ internal LoggingAspectHandler ( string service, LogLevel? logLevel, + LoggerOutputCase? loggerOutputCase, double? samplingRate, bool? logEvent, string correlationIdPath, @@ -114,6 +121,7 @@ ISystemWrapper systemWrapper { _service = service; _logLevel = logLevel; + _loggerOutputCase = loggerOutputCase; _samplingRate = samplingRate; _logEvent = logEvent; _clearState = clearState; @@ -135,7 +143,8 @@ public void OnEntry(AspectEventArgs eventArgs) { Service = _service, MinimumLevel = _logLevel, - SamplingRate = _samplingRate + SamplingRate = _samplingRate, + LoggerOutputCase = _loggerOutputCase }; switch (Logger.LoggerProvider) diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingConstants.cs b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingConstants.cs index babe5aa51..1ff29c35f 100644 --- a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingConstants.cs +++ b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingConstants.cs @@ -26,6 +26,11 @@ internal static class LoggingConstants /// Constant for default log level /// internal const LogLevel DefaultLogLevel = LogLevel.Information; + + /// + /// Constant for default log output case + /// + internal const LoggerOutputCase DefaultLoggerOutputCase = LoggerOutputCase.SnakeCase; /// /// Constant for key json formatter diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsConfigurations.cs b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsConfigurations.cs index b56a82889..f05eca49c 100644 --- a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsConfigurations.cs +++ b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsConfigurations.cs @@ -41,4 +41,16 @@ internal static LogLevel GetLogLevel(this IPowertoolsConfigurations powertoolsCo return LoggingConstants.DefaultLogLevel; } + + internal static LoggerOutputCase GetLoggerOutputCase(this IPowertoolsConfigurations powertoolsConfigurations, + LoggerOutputCase? loggerOutputCase = null) + { + if (loggerOutputCase.HasValue) + return loggerOutputCase.Value; + + if (Enum.TryParse((powertoolsConfigurations.LoggerOutputCase ?? "").Trim(), true, out LoggerOutputCase result)) + return result; + + return LoggingConstants.DefaultLoggerOutputCase; + } } \ 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 abe523d2d..947dfb771 100644 --- a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsLogger.cs +++ b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsLogger.cs @@ -94,6 +94,8 @@ public PowertoolsLogger( ? CurrentConfig.Service : _powertoolsConfigurations.Service; + internal PowertoolsLoggerScope CurrentScope { get; private set; } + /// /// Begins the scope. /// @@ -102,7 +104,62 @@ public PowertoolsLogger( /// System.IDisposable. public IDisposable BeginScope(TState state) { - return default!; + CurrentScope = new PowertoolsLoggerScope(this, GetScopeKeys(state)); + return CurrentScope; + } + + /// + /// Ends the scope. + /// + internal void EndScope() + { + CurrentScope = null; + } + + /// + /// Extract provided scope keys + /// + /// The type of the t state. + /// The state. + /// Key/Value pair of provided scope keys + private static Dictionary GetScopeKeys(TState state) + { + var keys = new Dictionary(); + + if (state is null) + return keys; + + switch (state) + { + case IEnumerable> pairs: + { + foreach (var (key, value) in pairs) + { + if (!string.IsNullOrWhiteSpace(key)) + keys.TryAdd(key, value); + } + break; + } + case IEnumerable> pairs: + { + foreach (var (key, value) in pairs) + { + if (!string.IsNullOrWhiteSpace(key)) + keys.TryAdd(key, value); + } + break; + } + default: + { + foreach (var property in state.GetType().GetProperties()) + { + keys.TryAdd(property.Name, property.GetValue(state)); + } + break; + } + } + + return keys; } /// @@ -149,6 +206,16 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except message.TryAdd(LoggingConstants.KeyFunctionRequestId, PowertoolsLambdaContext.Instance.AwsRequestId); } + // Add Extra Fields + if (CurrentScope?.ExtraKeys is not null) + { + foreach (var (key, value) in CurrentScope.ExtraKeys) + { + if (!string.IsNullOrWhiteSpace(key)) + message.TryAdd(key, value); + } + } + message.TryAdd(LoggingConstants.KeyTimestamp, DateTime.UtcNow.ToString("o")); message.TryAdd(LoggingConstants.KeyLogLevel, logLevel.ToString()); message.TryAdd(LoggingConstants.KeyService, Service); @@ -166,35 +233,28 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except _systemWrapper.LogLine(JsonSerializer.Serialize(message, options)); } - internal JsonSerializerOptions BuildCaseSerializerOptions(){ - object LogCase; - Enum.TryParse(typeof(LoggerOutputCase), _currentConfig.LogOutputCase, true, out LogCase); - - if(LogCase != null){ - switch (LogCase) - { - case LoggerOutputCase.CamelCase: - return new(JsonSerializerDefaults.Web){ - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - DictionaryKeyPolicy = JsonNamingPolicy.CamelCase - }; - case LoggerOutputCase.PascalCase: - return new() { - PropertyNamingPolicy = PascalCaseNamingPolicy.Instance, - DictionaryKeyPolicy = PascalCaseNamingPolicy.Instance - }; - default: // Snake case is the default - return new() { - PropertyNamingPolicy = SnakeCaseNamingPolicy.Instance, - DictionaryKeyPolicy = SnakeCaseNamingPolicy.Instance - }; - } - } - else { - return new() { - PropertyNamingPolicy = SnakeCaseNamingPolicy.Instance, - DictionaryKeyPolicy = SnakeCaseNamingPolicy.Instance - }; + private JsonSerializerOptions BuildCaseSerializerOptions() + { + switch (CurrentConfig.LoggerOutputCase) + { + case LoggerOutputCase.CamelCase: + return new(JsonSerializerDefaults.Web) + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DictionaryKeyPolicy = JsonNamingPolicy.CamelCase + }; + case LoggerOutputCase.PascalCase: + return new() + { + PropertyNamingPolicy = PascalCaseNamingPolicy.Instance, + DictionaryKeyPolicy = PascalCaseNamingPolicy.Instance + }; + default: // Snake case is the default + return new() + { + PropertyNamingPolicy = SnakeCaseNamingPolicy.Instance, + DictionaryKeyPolicy = SnakeCaseNamingPolicy.Instance + }; } } @@ -215,14 +275,14 @@ private LoggerConfiguration GetCurrentConfig() var currConfig = _getCurrentConfig(); var minimumLevel = _powertoolsConfigurations.GetLogLevel(currConfig?.MinimumLevel); var samplingRate = currConfig?.SamplingRate ?? _powertoolsConfigurations.LoggerSampleRate; - var logOutputCase = _powertoolsConfigurations.LoggerOutputCase; + var loggerOutputCase = _powertoolsConfigurations.GetLoggerOutputCase(currConfig?.LoggerOutputCase); var config = new LoggerConfiguration { Service = currConfig?.Service, MinimumLevel = minimumLevel, SamplingRate = samplingRate, - LogOutputCase = logOutputCase + LoggerOutputCase = loggerOutputCase }; if (!samplingRate.HasValue) diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsLoggerScope.cs b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsLoggerScope.cs new file mode 100644 index 000000000..006f62938 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsLoggerScope.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; + +namespace AWS.Lambda.Powertools.Logging.Internal; + +/// +/// Class PowertoolsLoggerScope. +/// +internal class PowertoolsLoggerScope : IDisposable +{ + /// + /// The associated logger + /// + private readonly PowertoolsLogger _logger; + + /// + /// The provided extra keys + /// + internal Dictionary ExtraKeys { get; } + + /// + /// Creates a PowertoolsLoggerScope object + /// + internal PowertoolsLoggerScope(PowertoolsLogger logger, Dictionary extraKeys) + { + _logger = logger; + ExtraKeys = extraKeys; + } + + /// + /// Implements IDisposable interface + /// + public void Dispose() + { + _logger?.EndScope(); + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/Logger.cs b/libraries/src/AWS.Lambda.Powertools.Logging/Logger.cs index 4b2189fc0..0989abac4 100644 --- a/libraries/src/AWS.Lambda.Powertools.Logging/Logger.cs +++ b/libraries/src/AWS.Lambda.Powertools.Logging/Logger.cs @@ -149,7 +149,7 @@ internal static void RemoveAllKeys() #endregion - #region Logger Functions + #region Core Logger Methods #region Debug @@ -213,16 +213,6 @@ public static void LogDebug(string message, params object[] args) LoggerInstance.LogDebug(message, args); } - /// - /// Formats and writes a debug log message as JSON. - /// - /// The object to be serialized as JSON. - /// Logger.LogDebug(new {User = user, Address = address}) - public static void LogDebug(object message) - { - LoggerInstance.LogDebug(message); - } - #endregion #region Trace @@ -287,16 +277,6 @@ public static void LogTrace(string message, params object[] args) LoggerInstance.LogTrace(message, args); } - /// - /// Formats and writes a trace log message as JSON. - /// - /// The object to be serialized as JSON. - /// Logger.LogTrace(new {User = user, Address = address}) - public static void LogTrace(object message) - { - LoggerInstance.LogTrace(message); - } - #endregion #region Information @@ -361,16 +341,6 @@ public static void LogInformation(string message, params object[] args) LoggerInstance.LogInformation(message, args); } - /// - /// Formats and writes an informational log message as JSON. - /// - /// The object to be serialized as JSON. - /// Logger.LogInformation(new {User = user, Address = address}) - public static void LogInformation(object message) - { - LoggerInstance.LogInformation(message); - } - #endregion #region Warning @@ -434,17 +404,7 @@ public static void LogWarning(string message, params object[] args) { LoggerInstance.LogWarning(message, args); } - - /// - /// Formats and writes a warning log message as JSON. - /// - /// The object to be serialized as JSON. - /// Logger.LogWarning(new {User = user, Address = address}) - public static void LogWarning(object message) - { - LoggerInstance.LogWarning(message); - } - + #endregion #region Error @@ -510,16 +470,6 @@ public static void LogError(string message, params object[] args) LoggerInstance.LogError(message, args); } - /// - /// Formats and writes an error log message as JSON. - /// - /// The object to be serialized as JSON. - /// Logger.LogError(new {User = user, Address = address}) - public static void LogError(object message) - { - LoggerInstance.LogError(message); - } - #endregion #region Critical @@ -583,17 +533,7 @@ public static void LogCritical(string message, params object[] args) { LoggerInstance.LogCritical(message, args); } - - /// - /// Formats and writes a critical log message as JSON. - /// - /// The object to be serialized as JSON. - /// Logger.LogCritical(new {User = user, Address = address}) - public static void LogCritical(object message) - { - LoggerInstance.LogCritical(message); - } - + #endregion #region Log @@ -665,17 +605,552 @@ public static void Log(LogLevel logLevel, EventId eventId, TState state, LoggerInstance.Log(logLevel, eventId, state, exception, formatter); } + #endregion + + #endregion + + #region JSON Logger Methods + + /// + /// Formats and writes a trace log message as JSON. + /// + /// The object to be serialized as JSON. + /// logger.LogTrace(new {User = user, Address = address}) + public static void LogTrace(object message) + { + LoggerInstance.LogTrace(message); + } + + /// + /// Formats and writes an trace log message. + /// + /// The exception to log. + /// logger.LogTrace(exception) + public static void LogTrace(Exception exception) + { + LoggerInstance.LogTrace(exception); + } + + /// + /// Formats and writes a debug log message as JSON. + /// + /// The object to be serialized as JSON. + /// logger.LogDebug(new {User = user, Address = address}) + public static void LogDebug(object message) + { + LoggerInstance.LogDebug(message); + } + + /// + /// Formats and writes an debug log message. + /// + /// The exception to log. + /// logger.LogDebug(exception) + public static void LogDebug(Exception exception) + { + LoggerInstance.LogDebug(exception); + } + + /// + /// Formats and writes an information log message as JSON. + /// + /// The object to be serialized as JSON. + /// logger.LogInformation(new {User = user, Address = address}) + public static void LogInformation(object message) + { + LoggerInstance.LogInformation(message); + } + + /// + /// Formats and writes an information log message. + /// + /// The exception to log. + /// logger.LogInformation(exception) + public static void LogInformation(Exception exception) + { + LoggerInstance.LogInformation(exception); + } + + /// + /// Formats and writes a warning log message as JSON. + /// + /// The object to be serialized as JSON. + /// logger.LogWarning(new {User = user, Address = address}) + public static void LogWarning(object message) + { + LoggerInstance.LogWarning(message); + } + + /// + /// Formats and writes an warning log message. + /// + /// The exception to log. + /// logger.LogWarning(exception) + public static void LogWarning(Exception exception) + { + LoggerInstance.LogWarning(exception); + } + + /// + /// Formats and writes a error log message as JSON. + /// + /// The object to be serialized as JSON. + /// logger.LogCritical(new {User = user, Address = address}) + public static void LogError(object message) + { + LoggerInstance.LogError(message); + } + + /// + /// Formats and writes an error log message. + /// + /// The exception to log. + /// logger.LogError(exception) + public static void LogError(Exception exception) + { + LoggerInstance.LogError(exception); + } + + /// + /// Formats and writes a critical log message as JSON. + /// + /// The object to be serialized as JSON. + /// logger.LogCritical(new {User = user, Address = address}) + public static void LogCritical(object message) + { + LoggerInstance.LogCritical(message); + } + + /// + /// Formats and writes an critical log message. + /// + /// The exception to log. + /// logger.LogCritical(exception) + public static void LogCritical(Exception exception) + { + LoggerInstance.LogCritical(exception); + } + /// /// Formats and writes a log message as JSON at the specified log level. /// /// Entry will be written on this level. /// The object to be serialized as JSON. - /// Logger.Log(LogLevel.Information, new {User = user, Address = address}) + /// logger.Log(LogLevel.Information, new {User = user, Address = address}) public static void Log(LogLevel logLevel, object message) { LoggerInstance.Log(logLevel, message); } + /// + /// Formats and writes a log message at the specified log level. + /// + /// Entry will be written on this level. + /// The exception to log. + /// logger.Log(LogLevel.Information, exception) + public static void Log(LogLevel logLevel, Exception exception) + { + LoggerInstance.Log(logLevel, exception); + } + + #endregion + + #region ExtraKeys Logger Methods + + #region Debug + + /// + /// Formats and writes a debug log message. + /// + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// The exception to log. + /// 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 + { + LoggerInstance.LogDebug(extraKeys, eventId, exception, message, args); + } + + /// + /// Formats and writes a debug log message. + /// + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// 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 + { + LoggerInstance.LogDebug(extraKeys, eventId, message, args); + } + + /// + /// Formats and writes a debug log message. + /// + /// Additional keys will be appended to the log entry. + /// The exception to log. + /// 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 + { + LoggerInstance.LogDebug(extraKeys, exception, message, args); + } + + /// + /// Formats and writes a debug log message. + /// + /// Additional keys will be appended to the log entry. + /// 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 + { + LoggerInstance.LogDebug(extraKeys, message, args); + } + + #endregion + + #region Trace + + /// + /// Formats and writes a trace log message. + /// + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// The exception to log. + /// 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 + { + LoggerInstance.LogTrace(extraKeys, eventId, exception, message, args); + } + + /// + /// Formats and writes a trace log message. + /// + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// 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 + { + LoggerInstance.LogTrace(extraKeys, eventId, message, args); + } + + /// + /// Formats and writes a trace log message. + /// + /// Additional keys will be appended to the log entry. + /// The exception to log. + /// 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 + { + LoggerInstance.LogTrace(extraKeys, exception, message, args); + } + + /// + /// Formats and writes a trace log message. + /// + /// Additional keys will be appended to the log entry. + /// 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 + { + LoggerInstance.LogTrace(extraKeys, message, args); + } + + #endregion + + #region Information + + /// + /// Formats and writes an informational log message. + /// + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// The exception to log. + /// 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 + { + LoggerInstance.LogInformation(extraKeys, eventId, exception, message, args); + } + + /// + /// Formats and writes an informational log message. + /// + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// 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 + { + LoggerInstance.LogInformation(extraKeys, eventId, message, args); + } + + /// + /// Formats and writes an informational log message. + /// + /// Additional keys will be appended to the log entry. + /// The exception to log. + /// 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 + { + LoggerInstance.LogInformation(extraKeys, exception, message, args); + } + + /// + /// Formats and writes an informational log message. + /// + /// Additional keys will be appended to the log entry. + /// 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 + { + LoggerInstance.LogInformation(extraKeys, message, args); + } + + #endregion + + #region Warning + + /// + /// Formats and writes a warning log message. + /// + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// The exception to log. + /// 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 + { + LoggerInstance.LogWarning(extraKeys, eventId, exception, message, args); + } + + /// + /// Formats and writes a warning log message. + /// + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// 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 + { + LoggerInstance.LogWarning(extraKeys, eventId, message, args); + } + + /// + /// Formats and writes a warning log message. + /// + /// Additional keys will be appended to the log entry. + /// The exception to log. + /// 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 + { + LoggerInstance.LogWarning(extraKeys, exception, message, args); + } + + /// + /// Formats and writes a warning log message. + /// + /// Additional keys will be appended to the log entry. + /// 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 + { + LoggerInstance.LogWarning(extraKeys, message, args); + } + + #endregion + + #region Error + + /// + /// Formats and writes an error log message. + /// + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// The exception to log. + /// 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 + { + LoggerInstance.LogError(extraKeys, eventId, exception, message, args); + } + + /// + /// Formats and writes an error log message. + /// + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// 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 + { + LoggerInstance.LogError(extraKeys, eventId, message, args); + } + + /// + /// Formats and writes an error log message. + /// + /// Additional keys will be appended to the log entry. + /// The exception to log. + /// 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 + { + LoggerInstance.LogError(extraKeys, exception, message, args); + } + + /// + /// Formats and writes an error log message. + /// + /// Additional keys will be appended to the log entry. + /// 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 + { + LoggerInstance.LogError(extraKeys, message, args); + } + + #endregion + + #region Critical + + /// + /// Formats and writes a critical log message. + /// + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// The exception to log. + /// 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 + { + LoggerInstance.LogCritical(extraKeys, eventId, exception, message, args); + } + + /// + /// Formats and writes a critical log message. + /// + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// 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 + { + LoggerInstance.LogCritical(extraKeys, eventId, message, args); + } + + /// + /// Formats and writes a critical log message. + /// + /// Additional keys will be appended to the log entry. + /// The exception to log. + /// 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 + { + LoggerInstance.LogCritical(extraKeys, exception, message, args); + } + + /// + /// Formats and writes a critical log message. + /// + /// Additional keys will be appended to the log entry. + /// 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 + { + LoggerInstance.LogCritical(extraKeys, message, args); + } + + #endregion + + #region Log + + /// + /// Formats and writes a log message at the specified log level. + /// + /// Entry will be written on this level. + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// The exception to log. + /// 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 + { + LoggerInstance.Log(logLevel, extraKeys, eventId, exception, message, args); + } + + /// + /// Formats and writes a log message at the specified log level. + /// + /// Entry will be written on this level. + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// 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 + { + LoggerInstance.Log(logLevel, extraKeys, eventId, message, args); + } + + /// + /// Formats and writes a log message at the specified log level. + /// + /// Entry will be written on this level. + /// Additional keys will be appended to the log entry. + /// The exception to log. + /// 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 + { + LoggerInstance.Log(logLevel, extraKeys, exception, message, args); + } + + /// + /// Formats and writes a log message at the specified log level. + /// + /// Entry will be written on this level. + /// Additional keys will be appended to the log entry. + /// 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 + { + LoggerInstance.Log(logLevel, extraKeys, message, args); + } + #endregion #endregion diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/LoggerConfiguration.cs b/libraries/src/AWS.Lambda.Powertools.Logging/LoggerConfiguration.cs index c86b0f53d..da2297939 100644 --- a/libraries/src/AWS.Lambda.Powertools.Logging/LoggerConfiguration.cs +++ b/libraries/src/AWS.Lambda.Powertools.Logging/LoggerConfiguration.cs @@ -48,7 +48,7 @@ public class LoggerConfiguration : IOptions public double? SamplingRate { get; set; } /// - /// The default configured instance + /// The default configured options instance /// /// The value. LoggerConfiguration IOptions.Value => this; @@ -58,5 +58,5 @@ public class LoggerConfiguration : IOptions /// This can be also set using the environment variable POWERTOOLS_LOGGER_CASE. /// /// The logger output case. - public string LogOutputCase { get; set; } + public LoggerOutputCase? LoggerOutputCase { get; set; } } \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/LoggerExtensions.cs b/libraries/src/AWS.Lambda.Powertools.Logging/LoggerExtensions.cs index 574c4aa73..3c22c3da0 100644 --- a/libraries/src/AWS.Lambda.Powertools.Logging/LoggerExtensions.cs +++ b/libraries/src/AWS.Lambda.Powertools.Logging/LoggerExtensions.cs @@ -13,6 +13,7 @@ * permissions and limitations under the License. */ +using System; using AWS.Lambda.Powertools.Logging.Internal; using Microsoft.Extensions.Logging; @@ -23,6 +24,8 @@ namespace AWS.Lambda.Powertools.Logging; /// public static class LoggerExtensions { + #region JSON Logger Extentions + /// /// Formats and writes a trace log message as JSON. /// @@ -34,6 +37,17 @@ public static void LogTrace(this ILogger logger, object message) logger.LogTrace(LoggingConstants.KeyJsonFormatter, message); } + /// + /// Formats and writes an trace log message. + /// + /// The to write to. + /// The exception to log. + /// logger.LogTrace(exception) + public static void LogTrace(this ILogger logger, Exception exception) + { + logger.LogTrace(exception: exception, message: exception.Message); + } + /// /// Formats and writes a debug log message as JSON. /// @@ -45,6 +59,17 @@ public static void LogDebug(this ILogger logger, object message) logger.LogDebug(LoggingConstants.KeyJsonFormatter, message); } + /// + /// Formats and writes an debug log message. + /// + /// The to write to. + /// The exception to log. + /// logger.LogDebug(exception) + public static void LogDebug(this ILogger logger, Exception exception) + { + logger.LogDebug(exception: exception, message: exception.Message); + } + /// /// Formats and writes an information log message as JSON. /// @@ -56,6 +81,17 @@ public static void LogInformation(this ILogger logger, object message) logger.LogInformation(LoggingConstants.KeyJsonFormatter, message); } + /// + /// Formats and writes an information log message. + /// + /// The to write to. + /// The exception to log. + /// logger.LogInformation(exception) + public static void LogInformation(this ILogger logger, Exception exception) + { + logger.LogInformation(exception: exception, message: exception.Message); + } + /// /// Formats and writes a warning log message as JSON. /// @@ -68,16 +104,38 @@ public static void LogWarning(this ILogger logger, object message) } /// - /// Formats and writes an error log message as JSON. + /// Formats and writes an warning log message. + /// + /// The to write to. + /// The exception to log. + /// logger.LogWarning(exception) + public static void LogWarning(this ILogger logger, Exception exception) + { + logger.LogWarning(exception: exception, message: exception.Message); + } + + /// + /// Formats and writes a error log message as JSON. /// /// The to write to. /// The object to be serialized as JSON. - /// logger.LogError(new {User = user, Address = address}) + /// logger.LogCritical(new {User = user, Address = address}) public static void LogError(this ILogger logger, object message) { logger.LogError(LoggingConstants.KeyJsonFormatter, message); } + /// + /// Formats and writes an error log message. + /// + /// The to write to. + /// The exception to log. + /// logger.LogError(exception) + public static void LogError(this ILogger logger, Exception exception) + { + logger.LogError(exception: exception, message: exception.Message); + } + /// /// Formats and writes a critical log message as JSON. /// @@ -89,6 +147,17 @@ public static void LogCritical(this ILogger logger, object message) logger.LogCritical(LoggingConstants.KeyJsonFormatter, message); } + /// + /// Formats and writes an critical log message. + /// + /// The to write to. + /// The exception to log. + /// logger.LogCritical(exception) + public static void LogCritical(this ILogger logger, Exception exception) + { + logger.LogCritical(exception: exception, message: exception.Message); + } + /// /// Formats and writes a log message as JSON at the specified log level. /// @@ -100,4 +169,471 @@ public static void Log(this ILogger logger, LogLevel logLevel, object message) { logger.Log(logLevel, LoggingConstants.KeyJsonFormatter, message); } + + /// + /// Formats and writes a log message at the specified log level. + /// + /// The to write to. + /// Entry will be written on this level. + /// The exception to log. + /// logger.Log(LogLevel.Information, exception) + public static void Log(this ILogger logger, LogLevel logLevel, Exception exception) + { + logger.Log(logLevel, exception: exception, message: exception.Message); + } + + #endregion + + #region ExtraKeys Logger Extentions + + #region Debug + + /// + /// Formats and writes a debug log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// The exception to log. + /// 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(this ILogger logger, T extraKeys, EventId eventId, Exception exception, + string message, params object[] args) where T : class + { + Log(logger, LogLevel.Debug, extraKeys, eventId, exception, message, args); + } + + /// + /// Formats and writes a debug log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// 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(this ILogger logger, T extraKeys, EventId eventId, string message, + params object[] args) where T : class + { + Log(logger, LogLevel.Debug, extraKeys, eventId, message, args); + } + + /// + /// Formats and writes a debug log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// The exception to log. + /// 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(this ILogger logger, T extraKeys, Exception exception, string message, + params object[] args) where T : class + { + Log(logger, LogLevel.Debug, extraKeys, exception, message, args); + } + + /// + /// Formats and writes a debug log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// 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 + { + Log(logger, LogLevel.Debug, extraKeys, message, args); + } + + #endregion + + #region Trace + + /// + /// Formats and writes a trace log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// The exception to log. + /// 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(this ILogger logger, T extraKeys, EventId eventId, Exception exception, + string message, params object[] args) where T : class + { + Log(logger, LogLevel.Trace, extraKeys, eventId, exception, message, args); + } + + /// + /// Formats and writes a trace log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// 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(this ILogger logger, T extraKeys, EventId eventId, string message, + params object[] args) where T : class + { + Log(logger, LogLevel.Trace, extraKeys, eventId, message, args); + } + + /// + /// Formats and writes a trace log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// The exception to log. + /// 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(this ILogger logger, T extraKeys, Exception exception, string message, + params object[] args) where T : class + { + Log(logger, LogLevel.Trace, extraKeys, exception, message, args); + } + + /// + /// Formats and writes a trace log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// 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 + { + Log(logger, LogLevel.Trace, extraKeys, message, args); + } + + #endregion + + #region Information + + /// + /// Formats and writes an informational log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// The exception to log. + /// 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(this ILogger logger, T extraKeys, EventId eventId, Exception exception, + string message, params object[] args) where T : class + { + Log(logger, LogLevel.Information, extraKeys, eventId, exception, message, args); + } + + /// + /// Formats and writes an informational log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// 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(this ILogger logger, T extraKeys, EventId eventId, string message, + params object[] args) where T : class + { + Log(logger, LogLevel.Information, extraKeys, eventId, message, args); + } + + /// + /// Formats and writes an informational log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// The exception to log. + /// 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(this ILogger logger, T extraKeys, Exception exception, string message, + params object[] args) where T : class + { + Log(logger, LogLevel.Information, extraKeys, exception, message, args); + } + + /// + /// Formats and writes an informational log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// 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 + { + Log(logger, LogLevel.Information, extraKeys, message, args); + } + + #endregion + + #region Warning + + /// + /// Formats and writes a warning log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// The exception to log. + /// 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(this ILogger logger, T extraKeys, EventId eventId, Exception exception, + string message, params object[] args) where T : class + { + Log(logger, LogLevel.Warning, extraKeys, eventId, exception, message, args); + } + + /// + /// Formats and writes a warning log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// 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(this ILogger logger, T extraKeys, EventId eventId, string message, + params object[] args) where T : class + { + Log(logger, LogLevel.Warning, extraKeys, eventId, message, args); + } + + /// + /// Formats and writes a warning log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// The exception to log. + /// 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(this ILogger logger, T extraKeys, Exception exception, string message, + params object[] args) where T : class + { + Log(logger, LogLevel.Warning, extraKeys, exception, message, args); + } + + /// + /// Formats and writes a warning log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// 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 + { + Log(logger, LogLevel.Warning, extraKeys, message, args); + } + + #endregion + + #region Error + + /// + /// Formats and writes an error log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// The exception to log. + /// 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(this ILogger logger, T extraKeys, EventId eventId, Exception exception, + string message, params object[] args) where T : class + { + Log(logger, LogLevel.Error, extraKeys, eventId, exception, message, args); + } + + /// + /// Formats and writes an error log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// 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(this ILogger logger, T extraKeys, EventId eventId, string message, + params object[] args) where T : class + { + Log(logger, LogLevel.Error, extraKeys, eventId, message, args); + } + + /// + /// Formats and writes an error log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// The exception to log. + /// 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(this ILogger logger, T extraKeys, Exception exception, string message, + params object[] args) where T : class + { + Log(logger, LogLevel.Error, extraKeys, exception, message, args); + } + + /// + /// Formats and writes an error log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// 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 + { + Log(logger, LogLevel.Error, extraKeys, message, args); + } + + #endregion + + #region Critical + + /// + /// Formats and writes a critical log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// The exception to log. + /// 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(this ILogger logger, T extraKeys, EventId eventId, Exception exception, + string message, params object[] args) where T : class + { + Log(logger, LogLevel.Critical, extraKeys, eventId, exception, message, args); + } + + /// + /// Formats and writes a critical log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// 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(this ILogger logger, T extraKeys, EventId eventId, string message, + params object[] args) where T : class + { + Log(logger, LogLevel.Critical, extraKeys, eventId, message, args); + } + + /// + /// Formats and writes a critical log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// The exception to log. + /// 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(this ILogger logger, T extraKeys, Exception exception, string message, + params object[] args) where T : class + { + Log(logger, LogLevel.Critical, extraKeys, exception, message, args); + } + + /// + /// Formats and writes a critical log message. + /// + /// The to write to. + /// Additional keys will be appended to the log entry. + /// 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 + { + Log(logger, LogLevel.Critical, extraKeys, message, args); + } + + #endregion + + #region Log + + /// + /// Formats and writes a log message at the specified log level. + /// + /// The to write to. + /// Entry will be written on this level. + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// The exception to log. + /// 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(this ILogger logger, LogLevel logLevel, T extraKeys, EventId eventId, Exception exception, + string message, params object[] args) where T : class + { + if (extraKeys is not null) + using (logger.BeginScope(extraKeys)) + logger.Log(logLevel, eventId, exception, message, args); + else + logger.Log(logLevel, eventId, exception, message, args); + } + + /// + /// Formats and writes a log message at the specified log level. + /// + /// The to write to. + /// Entry will be written on this level. + /// Additional keys will be appended to the log entry. + /// The event id associated with the log. + /// 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(this ILogger logger, LogLevel logLevel, T extraKeys, EventId eventId, string message, + params object[] args) where T : class + { + Log(logger, logLevel, extraKeys, eventId, null, message, args); + } + + /// + /// Formats and writes a log message at the specified log level. + /// + /// The to write to. + /// Entry will be written on this level. + /// Additional keys will be appended to the log entry. + /// The exception to log. + /// 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(this ILogger logger, LogLevel logLevel, T extraKeys, Exception exception, string message, + params object[] args) where T : class + { + Log(logger, logLevel, extraKeys, 0, exception, message, args); + } + + /// + /// Formats and writes a log message at the specified log level. + /// + /// The to write to. + /// Entry will be written on this level. + /// Additional keys will be appended to the log entry. + /// 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 + { + Log(logger, logLevel, extraKeys, 0, null, message, args); + } + + #endregion + + #endregion } \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/LoggingAttribute.cs b/libraries/src/AWS.Lambda.Powertools.Logging/LoggingAttribute.cs index c898f752d..80c26aa0f 100644 --- a/libraries/src/AWS.Lambda.Powertools.Logging/LoggingAttribute.cs +++ b/libraries/src/AWS.Lambda.Powertools.Logging/LoggingAttribute.cs @@ -37,6 +37,11 @@ public class LoggingAttribute : MethodAspectAttribute /// The log level /// private LogLevel? _logLevel; + + /// + /// The logger output case + /// + private LoggerOutputCase? _loggerOutputCase; /// /// The sampling rate @@ -100,6 +105,17 @@ public bool LogEvent /// /// true if [clear state]; otherwise, false. public bool ClearState { get; set; } = false; + + /// + /// Specify output case for logging (SnakeCase, by default). + /// This can be also set using the environment variable POWERTOOLS_LOGGER_CASE. + /// + /// The log level. + public LoggerOutputCase LoggerOutputCase + { + get => _loggerOutputCase ?? LoggingConstants.DefaultLoggerOutputCase; + set => _loggerOutputCase = value; + } /// /// Creates the handler. @@ -111,6 +127,7 @@ protected override IMethodAspectHandler CreateHandler() ( Service, _logLevel, + _loggerOutputCase, _samplingRate, _logEvent, CorrelationIdPath, diff --git a/libraries/src/AWS.Lambda.Powertools.Metrics/Serializer/StringEnumConverter.cs b/libraries/src/AWS.Lambda.Powertools.Metrics/Serializer/StringEnumConverter.cs index 063c02335..28c0d54b9 100644 --- a/libraries/src/AWS.Lambda.Powertools.Metrics/Serializer/StringEnumConverter.cs +++ b/libraries/src/AWS.Lambda.Powertools.Metrics/Serializer/StringEnumConverter.cs @@ -82,7 +82,7 @@ public override bool CanConvert(Type typeToConvert) /// /// The type handled by the converter. /// The serialization options to use. - /// A converter for which is compatible with . + /// A converter for which type is compatible with . public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) { var query = from field in typeToConvert.GetFields(BindingFlags.Public | BindingFlags.Static) diff --git a/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/LoggingAttributeTest.cs b/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/LoggingAttributeTest.cs index 3f82643ff..55b0134b7 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/LoggingAttributeTest.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/LoggingAttributeTest.cs @@ -49,7 +49,7 @@ public void OnEntry_WhenLambdaContextDoesNotExist_IgnoresLambdaContext() }; LoggingAspectHandler.ResetForTest(); - var handler = new LoggingAspectHandler(service, logLevel, null, true, null, true, configurations.Object, + var handler = new LoggingAspectHandler(service, logLevel, null, null, true, null, true, configurations.Object, systemWrapper.Object); // Act @@ -95,7 +95,7 @@ public void OnEntry_WhenLambdaContextDoesNotExist_IgnoresLambdaContextAndLogDebu }; LoggingAspectHandler.ResetForTest(); - var handler = new LoggingAspectHandler(service, logLevel, null, true, null, true, configurations.Object, + var handler = new LoggingAspectHandler(service, logLevel, null, null, true, null, true, configurations.Object, systemWrapper.Object); // Act @@ -141,7 +141,7 @@ public void OnEntry_WhenEventArgDoesNotExist_DoesNotLogEventArg() }; LoggingAspectHandler.ResetForTest(); - var handler = new LoggingAspectHandler(service, logLevel, null, true, null, true, configurations.Object, + var handler = new LoggingAspectHandler(service, logLevel, null, null, true, null, true, configurations.Object, systemWrapper.Object); // Act @@ -175,7 +175,7 @@ public void OnEntry_WhenEventArgDoesNotExist_DoesNotLogEventArgAndLogDebug() }; LoggingAspectHandler.ResetForTest(); - var handler = new LoggingAspectHandler(service, logLevel, null, true, null, true, configurations.Object, + var handler = new LoggingAspectHandler(service, logLevel, null, null, true, null, true, configurations.Object, systemWrapper.Object); // Act @@ -209,7 +209,7 @@ public void OnExit_WhenHandler_ClearKeys() }; LoggingAspectHandler.ResetForTest(); - var handler = new LoggingAspectHandler(service, logLevel, null, true, null, true, configurations.Object, + var handler = new LoggingAspectHandler(service, logLevel, null, null, true, null, true, configurations.Object, systemWrapper.Object); // Act @@ -248,7 +248,7 @@ protected void OnEntry_WhenEventArgExists_CapturesCorrelationIdBase(string corre }; LoggingAspectHandler.ResetForTest(); - var handler = new LoggingAspectHandler(service, logLevel, null, false, correlationIdPath, + var handler = new LoggingAspectHandler(service, logLevel, null, null, false, correlationIdPath, true, configurations.Object, systemWrapper.Object); diff --git a/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerTest.cs b/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerTest.cs index d2c78dad1..4dc00716e 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerTest.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerTest.cs @@ -14,7 +14,9 @@ */ using System; +using System.Collections.Generic; using System.Globalization; +using System.Linq; using AWS.Lambda.Powertools.Common; using AWS.Lambda.Powertools.Logging.Internal; using Microsoft.Extensions.Logging; @@ -25,7 +27,7 @@ namespace AWS.Lambda.Powertools.Logging.Tests { public class PowertoolsLoggerTest { - private void Log_WhenMinimumLevelIsBelowLogLevel_Logs(LogLevel logLevel, LogLevel minimumLevel) + private static void Log_WhenMinimumLevelIsBelowLogLevel_Logs(LogLevel logLevel, LogLevel minimumLevel) { // Arrange var loggerName = Guid.NewGuid().ToString(); @@ -45,22 +47,22 @@ private void Log_WhenMinimumLevelIsBelowLogLevel_Logs(LogLevel logLevel, LogLeve { // Act case LogLevel.Critical: - LoggerExtensions.LogCritical(logger, "Test"); + logger.LogCritical("Test"); break; case LogLevel.Debug: - LoggerExtensions.LogDebug(logger, "Test"); + logger.LogDebug("Test"); break; case LogLevel.Error: - LoggerExtensions.LogError(logger, "Test"); + logger.LogError("Test"); break; case LogLevel.Information: - LoggerExtensions.LogInformation(logger, "Test"); + logger.LogInformation("Test"); break; case LogLevel.Trace: - LoggerExtensions.LogTrace(logger, "Test"); + logger.LogTrace("Test"); break; case LogLevel.Warning: - LoggerExtensions.LogWarning(logger, "Test"); + logger.LogWarning("Test"); break; case LogLevel.None: break; @@ -76,7 +78,7 @@ private void Log_WhenMinimumLevelIsBelowLogLevel_Logs(LogLevel logLevel, LogLeve } - private void Log_WhenMinimumLevelIsAboveLogLevel_DoesNotLog(LogLevel logLevel, LogLevel minimumLevel) + private static void Log_WhenMinimumLevelIsAboveLogLevel_DoesNotLog(LogLevel logLevel, LogLevel minimumLevel) { // Arrange var loggerName = Guid.NewGuid().ToString(); @@ -96,22 +98,22 @@ private void Log_WhenMinimumLevelIsAboveLogLevel_DoesNotLog(LogLevel logLevel, L { // Act case LogLevel.Critical: - LoggerExtensions.LogCritical(logger, "Test"); + logger.LogCritical("Test"); break; case LogLevel.Debug: - LoggerExtensions.LogDebug(logger, "Test"); + logger.LogDebug("Test"); break; case LogLevel.Error: - LoggerExtensions.LogError(logger, "Test"); + logger.LogError("Test"); break; case LogLevel.Information: - LoggerExtensions.LogInformation(logger, "Test"); + logger.LogInformation("Test"); break; case LogLevel.Trace: - LoggerExtensions.LogTrace(logger, "Test"); + logger.LogTrace("Test"); break; case LogLevel.Warning: - LoggerExtensions.LogWarning(logger, "Test"); + logger.LogWarning("Test"); break; case LogLevel.None: break; @@ -268,7 +270,7 @@ public void Log_ConfigurationIsNotProvided_ReadsFromEnvironmentVariables() MinimumLevel = null }); - LoggerExtensions.LogInformation(logger, "Test"); + logger.LogInformation("Test"); // Assert systemWrapper.Verify(v => @@ -306,7 +308,7 @@ public void Log_SamplingRateGreaterThanRandom_ChangedLogLevelToDebug() MinimumLevel = null }); - LoggerExtensions.LogInformation(logger, "Test"); + logger.LogInformation("Test"); // Assert systemWrapper.Verify(v => @@ -342,7 +344,7 @@ public void Log_SamplingRateGreaterThanOne_SkipsSamplingRateConfiguration() MinimumLevel = null }); - LoggerExtensions.LogInformation(logger, "Test"); + logger.LogInformation("Test"); // Assert systemWrapper.Verify(v => @@ -367,7 +369,7 @@ public void Log_SetsCaseToCamelCase_OutputsCamelCaseLog() var configurations = new Mock(); configurations.Setup(c => c.Service).Returns(service); configurations.Setup(c => c.LogLevel).Returns(logLevel.ToString); - configurations.Setup(c => c.LoggerOutputCase).Returns("CamelCase"); + configurations.Setup(c => c.LoggerOutputCase).Returns(LoggerOutputCase.CamelCase.ToString); var systemWrapper = new Mock(); systemWrapper.Setup(c => c.GetRandom()).Returns(randomSampleRate); @@ -384,7 +386,7 @@ public void Log_SetsCaseToCamelCase_OutputsCamelCaseLog() PropTwo = "Value 2" }; - LoggerExtensions.LogInformation(logger, message); + logger.LogInformation(message); // Assert systemWrapper.Verify(v => @@ -408,7 +410,7 @@ public void Log_SetsCaseToPascalCase_OutputsPascalCaseLog() var configurations = new Mock(); configurations.Setup(c => c.Service).Returns(service); configurations.Setup(c => c.LogLevel).Returns(logLevel.ToString); - configurations.Setup(c => c.LoggerOutputCase).Returns("PascalCase"); + configurations.Setup(c => c.LoggerOutputCase).Returns(LoggerOutputCase.PascalCase.ToString); var systemWrapper = new Mock(); systemWrapper.Setup(c => c.GetRandom()).Returns(randomSampleRate); @@ -426,7 +428,7 @@ public void Log_SetsCaseToPascalCase_OutputsPascalCaseLog() PropTwo = "Value 2" }; - LoggerExtensions.LogInformation(logger, message); + logger.LogInformation(message); // Assert systemWrapper.Verify(v => @@ -450,7 +452,7 @@ public void Log_SetsCaseToSnakeCase_OutputsSnakeCaseLog() var configurations = new Mock(); configurations.Setup(c => c.Service).Returns(service); configurations.Setup(c => c.LogLevel).Returns(logLevel.ToString); - configurations.Setup(c => c.LoggerOutputCase).Returns("SnakeCase"); + configurations.Setup(c => c.LoggerOutputCase).Returns(LoggerOutputCase.SnakeCase.ToString); var systemWrapper = new Mock(); systemWrapper.Setup(c => c.GetRandom()).Returns(randomSampleRate); @@ -468,7 +470,7 @@ public void Log_SetsCaseToSnakeCase_OutputsSnakeCaseLog() PropTwo = "Value 2" }; - LoggerExtensions.LogInformation(logger, message); + logger.LogInformation(message); // Assert systemWrapper.Verify(v => @@ -509,7 +511,7 @@ public void Log_NoOutputCaseSet_OutputDefaultsToSnakeCaseLog() PropTwo = "Value 2" }; - LoggerExtensions.LogInformation(logger, message); + logger.LogInformation(message); // Assert systemWrapper.Verify(v => @@ -520,5 +522,365 @@ public void Log_NoOutputCaseSet_OutputDefaultsToSnakeCaseLog() ) ), Times.Once); } + + [Fact] + public void BeginScope_WhenScopeIsObject_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 scopeKeys = new + { + PropOne = "Value 1", + PropTwo = "Value 2" + }; + + using (var loggerScope = logger.BeginScope(scopeKeys) as PowertoolsLoggerScope) + { + 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.Null(logger.CurrentScope?.ExtraKeys); + } + + [Fact] + public void BeginScope_WhenScopeIsObjectDictionary_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 scopeKeys = new Dictionary + { + { "PropOne", "Value 1" }, + { "PropTwo", "Value 2" } + }; + + using (var loggerScope = logger.BeginScope(scopeKeys) as PowertoolsLoggerScope) + { + Assert.NotNull(loggerScope); + Assert.NotNull(loggerScope.ExtraKeys); + Assert.True(loggerScope.ExtraKeys.Count == 2); + Assert.True(loggerScope.ExtraKeys.ContainsKey("PropOne")); + Assert.True(loggerScope.ExtraKeys["PropOne"] == scopeKeys["PropOne"]); + Assert.True(loggerScope.ExtraKeys.ContainsKey("PropTwo")); + Assert.True(loggerScope.ExtraKeys["PropTwo"] == scopeKeys["PropTwo"]); + } + Assert.Null(logger.CurrentScope?.ExtraKeys); + } + + [Fact] + public void BeginScope_WhenScopeIsStringDictionary_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 scopeKeys = new Dictionary + { + { "PropOne", "Value 1" }, + { "PropTwo", "Value 2" } + }; + + using (var loggerScope = logger.BeginScope(scopeKeys) as PowertoolsLoggerScope) + { + 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.Null(logger.CurrentScope?.ExtraKeys); + } + + [Theory] + [InlineData(LogLevel.Trace, true)] + [InlineData(LogLevel.Debug, true)] + [InlineData(LogLevel.Information, true)] + [InlineData(LogLevel.Warning, true)] + [InlineData(LogLevel.Error, true)] + [InlineData(LogLevel.Critical, true)] + [InlineData(LogLevel.Trace, false)] + [InlineData(LogLevel.Debug, false)] + [InlineData(LogLevel.Information, false)] + [InlineData(LogLevel.Warning, false)] + [InlineData(LogLevel.Error, false)] + [InlineData(LogLevel.Critical, false)] + public void Log_WhenExtraKeysIsObjectDictionary_AppendExtraKeys(LogLevel logLevel, bool logMethod) + { + // Arrange + var loggerName = Guid.NewGuid().ToString(); + var service = Guid.NewGuid().ToString(); + var message = Guid.NewGuid().ToString(); + + var configurations = new Mock(); + configurations.Setup(c => c.Service).Returns(service); + configurations.Setup(c => c.LogLevel).Returns(logLevel.ToString); + configurations.Setup(c => c.LoggerOutputCase).Returns(LoggerOutputCase.PascalCase.ToString); + var systemWrapper = new Mock(); + + var logger = new PowertoolsLogger(loggerName,configurations.Object, systemWrapper.Object, () => + new LoggerConfiguration + { + Service = service, + MinimumLevel = LogLevel.Trace, + }); + + var scopeKeys = new Dictionary + { + { "PropOne", "Value 1" }, + { "PropTwo", "Value 2" } + }; + + if(logMethod) + logger.Log(logLevel, scopeKeys, message); + else switch (logLevel) + { + case LogLevel.Trace: + logger.LogTrace(scopeKeys, message); + break; + case LogLevel.Debug: + logger.LogDebug(scopeKeys, message); + break; + case LogLevel.Information: + logger.LogInformation(scopeKeys, message); + break; + case LogLevel.Warning: + logger.LogWarning(scopeKeys, message); + break; + case LogLevel.Error: + logger.LogError(scopeKeys, message); + break; + case LogLevel.Critical: + logger.LogCritical(scopeKeys, message); + break; + case LogLevel.None: + break; + default: + throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null); + } + + systemWrapper.Verify(v => + v.LogLine( + It.Is + (s=> + s.Contains(scopeKeys.Keys.First()) && + s.Contains(scopeKeys.Keys.Last()) && + s.Contains(scopeKeys.Values.First().ToString()) && + s.Contains(scopeKeys.Values.Last().ToString()) + ) + ), Times.Once); + + Assert.Null(logger.CurrentScope?.ExtraKeys); + } + + [Theory] + [InlineData(LogLevel.Trace, true)] + [InlineData(LogLevel.Debug, true)] + [InlineData(LogLevel.Information, true)] + [InlineData(LogLevel.Warning, true)] + [InlineData(LogLevel.Error, true)] + [InlineData(LogLevel.Critical, true)] + [InlineData(LogLevel.Trace, false)] + [InlineData(LogLevel.Debug, false)] + [InlineData(LogLevel.Information, false)] + [InlineData(LogLevel.Warning, false)] + [InlineData(LogLevel.Error, false)] + [InlineData(LogLevel.Critical, false)] + public void Log_WhenExtraKeysIsStringDictionary_AppendExtraKeys(LogLevel logLevel, bool logMethod) + { + // Arrange + var loggerName = Guid.NewGuid().ToString(); + var service = Guid.NewGuid().ToString(); + var message = Guid.NewGuid().ToString(); + + var configurations = new Mock(); + configurations.Setup(c => c.Service).Returns(service); + configurations.Setup(c => c.LogLevel).Returns(logLevel.ToString); + configurations.Setup(c => c.LoggerOutputCase).Returns(LoggerOutputCase.PascalCase.ToString); + var systemWrapper = new Mock(); + + var logger = new PowertoolsLogger(loggerName,configurations.Object, systemWrapper.Object, () => + new LoggerConfiguration + { + Service = service, + MinimumLevel = LogLevel.Trace, + }); + + var scopeKeys = new Dictionary + { + { "PropOne", "Value 1" }, + { "PropTwo", "Value 2" } + }; + + if(logMethod) + logger.Log(logLevel, scopeKeys, message); + else switch (logLevel) + { + case LogLevel.Trace: + logger.LogTrace(scopeKeys, message); + break; + case LogLevel.Debug: + logger.LogDebug(scopeKeys, message); + break; + case LogLevel.Information: + logger.LogInformation(scopeKeys, message); + break; + case LogLevel.Warning: + logger.LogWarning(scopeKeys, message); + break; + case LogLevel.Error: + logger.LogError(scopeKeys, message); + break; + case LogLevel.Critical: + logger.LogCritical(scopeKeys, message); + break; + case LogLevel.None: + break; + default: + throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null); + } + + systemWrapper.Verify(v => + v.LogLine( + It.Is + (s=> + s.Contains(scopeKeys.Keys.First()) && + s.Contains(scopeKeys.Keys.Last()) && + s.Contains(scopeKeys.Values.First()) && + s.Contains(scopeKeys.Values.Last()) + ) + ), Times.Once); + + Assert.Null(logger.CurrentScope?.ExtraKeys); + } + + [Theory] + [InlineData(LogLevel.Trace, true)] + [InlineData(LogLevel.Debug, true)] + [InlineData(LogLevel.Information, true)] + [InlineData(LogLevel.Warning, true)] + [InlineData(LogLevel.Error, true)] + [InlineData(LogLevel.Critical, true)] + [InlineData(LogLevel.Trace, false)] + [InlineData(LogLevel.Debug, false)] + [InlineData(LogLevel.Information, false)] + [InlineData(LogLevel.Warning, false)] + [InlineData(LogLevel.Error, false)] + [InlineData(LogLevel.Critical, false)] + public void Log_WhenExtraKeysAsObject_AppendExtraKeys(LogLevel logLevel, bool logMethod) + { + // Arrange + var loggerName = Guid.NewGuid().ToString(); + var service = Guid.NewGuid().ToString(); + var message = Guid.NewGuid().ToString(); + + var configurations = new Mock(); + configurations.Setup(c => c.Service).Returns(service); + configurations.Setup(c => c.LogLevel).Returns(logLevel.ToString); + configurations.Setup(c => c.LoggerOutputCase).Returns(LoggerOutputCase.PascalCase.ToString); + var systemWrapper = new Mock(); + + var logger = new PowertoolsLogger(loggerName,configurations.Object, systemWrapper.Object, () => + new LoggerConfiguration + { + Service = service, + MinimumLevel = LogLevel.Trace, + }); + + var scopeKeys = new + { + PropOne = "Value 1", + PropTwo = "Value 2" + }; + + if(logMethod) + logger.Log(logLevel, scopeKeys, message); + else switch (logLevel) + { + case LogLevel.Trace: + logger.LogTrace(scopeKeys, message); + break; + case LogLevel.Debug: + logger.LogDebug(scopeKeys, message); + break; + case LogLevel.Information: + logger.LogInformation(scopeKeys, message); + break; + case LogLevel.Warning: + logger.LogWarning(scopeKeys, message); + break; + case LogLevel.Error: + logger.LogError(scopeKeys, message); + break; + case LogLevel.Critical: + logger.LogCritical(scopeKeys, message); + break; + case LogLevel.None: + break; + default: + throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null); + } + + systemWrapper.Verify(v => + v.LogLine( + It.Is + (s=> + s.Contains("PropOne") && + s.Contains("PropTwo") && + s.Contains(scopeKeys.PropOne) && + s.Contains(scopeKeys.PropTwo) + ) + ), Times.Once); + + Assert.Null(logger.CurrentScope?.ExtraKeys); + } } } \ No newline at end of file