diff --git a/.autover/changes/541b1775-ecf8-4e75-99c8-675924a5c34e.json b/.autover/changes/541b1775-ecf8-4e75-99c8-675924a5c34e.json new file mode 100644 index 000000000..8b7312c31 --- /dev/null +++ b/.autover/changes/541b1775-ecf8-4e75-99c8-675924a5c34e.json @@ -0,0 +1,11 @@ +{ + "Projects": [ + { + "Name": "Amazon.Lambda.Logging.AspNetCore", + "Type": "Patch", + "ChangelogMessages": [ + "Fixed the JSON log formatter ignoring IncludeCategory" + ] + } + ] +} diff --git a/Libraries/src/Amazon.Lambda.Logging.AspNetCore/LambdaILogger.cs b/Libraries/src/Amazon.Lambda.Logging.AspNetCore/LambdaILogger.cs index 2898a8472..96191465d 100644 --- a/Libraries/src/Amazon.Lambda.Logging.AspNetCore/LambdaILogger.cs +++ b/Libraries/src/Amazon.Lambda.Logging.AspNetCore/LambdaILogger.cs @@ -2,32 +2,32 @@ using System.Collections.Generic; namespace Microsoft.Extensions.Logging -{ - internal class LambdaILogger : ILogger - { - // Private fields - private readonly string _categoryName; - private readonly LambdaLoggerOptions _options; - - - internal IExternalScopeProvider ScopeProvider { get; set; } - - // Constructor - public LambdaILogger(string categoryName, LambdaLoggerOptions options) - { - _categoryName = categoryName; - _options = options; - } - - // ILogger methods - public IDisposable BeginScope(TState state) => ScopeProvider?.Push(state) ?? new NoOpDisposable(); - - public bool IsEnabled(LogLevel logLevel) - { - return ( - _options.Filter == null || - _options.Filter(_categoryName, logLevel)); - } +{ + internal class LambdaILogger : ILogger + { + // Private fields + private readonly string _categoryName; + private readonly LambdaLoggerOptions _options; + + + internal IExternalScopeProvider ScopeProvider { get; set; } + + // Constructor + public LambdaILogger(string categoryName, LambdaLoggerOptions options) + { + _categoryName = categoryName; + _options = options; + } + + // ILogger methods + public IDisposable BeginScope(TState state) => ScopeProvider?.Push(state) ?? new NoOpDisposable(); + + public bool IsEnabled(LogLevel logLevel) + { + return ( + _options.Filter == null || + _options.Filter(_categoryName, logLevel)); + } /// /// The Log method called by the ILogger framework to log message to logger's target. In the Lambda case the formatted logging will be @@ -39,17 +39,17 @@ public bool IsEnabled(LogLevel logLevel) /// /// /// - /// - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) - { - if (formatter == null) - { - throw new ArgumentNullException(nameof(formatter)); - } - - if (!IsEnabled(logLevel)) - { - return; + /// + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (formatter == null) + { + throw new ArgumentNullException(nameof(formatter)); + } + + if (!IsEnabled(logLevel)) + { + return; } var lambdaLogLevel = ConvertLogLevel(logLevel); @@ -75,6 +75,16 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except messageTemplate = formatter.Invoke(state, exception); } + if (_options.IncludeCategory) + { + // Unlike the text format, the JSON format otherwise drops the + // category entirely, so IncludeCategory has no effect. Prepend a + // "{Category}" placeholder (which the JSON formatter turns into a + // queryable property) and supply the category value. + messageTemplate = "[{Category}] " + messageTemplate; + parameters.Insert(0, _categoryName); + } + Amazon.Lambda.Core.LambdaLogger.Log(lambdaLogLevel, exception, messageTemplate, parameters.ToArray()); } else @@ -133,26 +143,26 @@ private static Amazon.Lambda.Core.LogLevel ConvertLogLevel(LogLevel logLevel) default: return Amazon.Lambda.Core.LogLevel.Information; } - } - - private void GetScopeInformation(List logMessageComponents) - { - var scopeProvider = ScopeProvider; - - if (_options.IncludeScopes && scopeProvider != null) - { - var initialCount = logMessageComponents.Count; - - scopeProvider.ForEachScope((scope, list) => - { - list.Add(scope.ToString()); - }, (logMessageComponents)); - - if (logMessageComponents.Count > initialCount) - { - logMessageComponents.Add("=>"); - } - } + } + + private void GetScopeInformation(List logMessageComponents) + { + var scopeProvider = ScopeProvider; + + if (_options.IncludeScopes && scopeProvider != null) + { + var initialCount = logMessageComponents.Count; + + scopeProvider.ForEachScope((scope, list) => + { + list.Add(scope.ToString()); + }, (logMessageComponents)); + + if (logMessageComponents.Count > initialCount) + { + logMessageComponents.Add("=>"); + } + } } private bool IsLambdaJsonFormatEnabled @@ -164,12 +174,12 @@ private bool IsLambdaJsonFormatEnabled } // Private classes - private class NoOpDisposable : IDisposable - { - public void Dispose() - { - } + private class NoOpDisposable : IDisposable + { + public void Dispose() + { + } } - + } } diff --git a/Libraries/test/Amazon.Lambda.Logging.AspNetCore.Tests/LoggingTests.cs b/Libraries/test/Amazon.Lambda.Logging.AspNetCore.Tests/LoggingTests.cs index e9997cdf3..51f1626ba 100644 --- a/Libraries/test/Amazon.Lambda.Logging.AspNetCore.Tests/LoggingTests.cs +++ b/Libraries/test/Amazon.Lambda.Logging.AspNetCore.Tests/LoggingTests.cs @@ -574,6 +574,76 @@ public void TestJSONParameterLogging() } + [Fact] + public void TestJSONParameterLoggingIncludesCategoryWhenEnabled() + { + Environment.SetEnvironmentVariable("AWS_LAMBDA_LOG_FORMAT", "JSON"); + try + { + using (var writer = new StringWriter()) + { + ConnectLoggingActionToLogger(message => writer.Write(message)); + + var configuration = new ConfigurationBuilder() + .AddJsonFile(GetAppSettingsPath("appsettings.json")) + .Build(); + + var loggerOptions = new LambdaLoggerOptions(configuration); + loggerOptions.IncludeCategory = true; + var loggerFactory = new TestLoggerFactory() + .AddLambdaLogger(loggerOptions); + + var logger = loggerFactory.CreateLogger("JSONLogging"); + + logger.LogError(new Exception("Too Cheap"), "User {name} fail to by {product} for {price}", "Gilmour", "Guitar", 55.55); + + var text = writer.ToString(); + // Category is prepended as a placeholder + argument, so it becomes + // a queryable JSON property instead of being dropped. + Assert.Contains("{Category}", text); + Assert.Contains("parameter count: 4", text); + } + } + finally + { + Environment.SetEnvironmentVariable("AWS_LAMBDA_LOG_FORMAT", null); + } + } + + [Fact] + public void TestJSONParameterLoggingOmitsCategoryWhenDisabled() + { + Environment.SetEnvironmentVariable("AWS_LAMBDA_LOG_FORMAT", "JSON"); + try + { + using (var writer = new StringWriter()) + { + ConnectLoggingActionToLogger(message => writer.Write(message)); + + var configuration = new ConfigurationBuilder() + .AddJsonFile(GetAppSettingsPath("appsettings.json")) + .Build(); + + var loggerOptions = new LambdaLoggerOptions(configuration); + loggerOptions.IncludeCategory = false; + var loggerFactory = new TestLoggerFactory() + .AddLambdaLogger(loggerOptions); + + var logger = loggerFactory.CreateLogger("JSONLogging"); + + logger.LogError(new Exception("Too Cheap"), "User {name} fail to by {product} for {price}", "Gilmour", "Guitar", 55.55); + + var text = writer.ToString(); + Assert.DoesNotContain("{Category}", text); + Assert.Contains("parameter count: 3", text); + } + } + finally + { + Environment.SetEnvironmentVariable("AWS_LAMBDA_LOG_FORMAT", null); + } + } + [Fact] public void JsonLoggingWithNoOriginalFormat() {