From 1f7df12793e80001af55a0d26a9a8f270f3578c3 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Wed, 11 Oct 2023 11:46:36 +0100 Subject: [PATCH 1/3] Add DateOnly and TimeOnly converters --- .../Internal/Converters/DateOnlyConverter.cs | 52 ++++++++++++ .../Internal/Converters/TimeOnlyConverter.cs | 52 ++++++++++++ .../Internal/LoggingAspectHandler.cs | 2 + .../Internal/PowertoolsLogger.cs | 2 + .../PowertoolsLoggerTest.cs | 82 +++++++++++++++++++ 5 files changed, 190 insertions(+) create mode 100644 libraries/src/AWS.Lambda.Powertools.Logging/Internal/Converters/DateOnlyConverter.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.Logging/Internal/Converters/TimeOnlyConverter.cs diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/Converters/DateOnlyConverter.cs b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/Converters/DateOnlyConverter.cs new file mode 100644 index 000000000..a6f969e59 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/Converters/DateOnlyConverter.cs @@ -0,0 +1,52 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace AWS.Lambda.Powertools.Logging.Internal.Converters; + +/// +/// DateOnly JSON converter +/// +public class DateOnlyConverter : JsonConverter +{ + private const string DateFormat = "yyyy-MM-dd"; + + /// + /// Converts DateOnly from JSON. + /// + /// + /// + /// + /// + public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return DateOnly.ParseExact(reader.GetString()!, DateFormat, CultureInfo.InvariantCulture); + } + + /// + /// Converts DateOnly to JSON. + /// + /// + /// + /// + public override void Write(Utf8JsonWriter writer, DateOnly value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString(DateFormat, CultureInfo.InvariantCulture)); + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/Converters/TimeOnlyConverter.cs b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/Converters/TimeOnlyConverter.cs new file mode 100644 index 000000000..737362ca0 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/Converters/TimeOnlyConverter.cs @@ -0,0 +1,52 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace AWS.Lambda.Powertools.Logging.Internal.Converters; + +/// +/// TimeOnly JSON converter +/// +internal class TimeOnlyConverter : JsonConverter +{ + private const string TimeFormat = "HH:mm:ss.FFFFFFF"; + + /// + /// Converts TimeOnly from JSON. + /// + /// + /// + /// + /// + public override TimeOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return TimeOnly.ParseExact(reader.GetString()!, TimeFormat, CultureInfo.InvariantCulture); + } + + /// + /// Converts TimeOnly to JSON. + /// + /// + /// + /// + public override void Write(Utf8JsonWriter writer, TimeOnly value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString(TimeFormat, CultureInfo.InvariantCulture)); + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingAspectHandler.cs b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingAspectHandler.cs index 4b2624902..441cdc222 100644 --- a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingAspectHandler.cs +++ b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingAspectHandler.cs @@ -288,6 +288,8 @@ private static JsonSerializerOptions BuildJsonSerializerOptions() jsonOptions.Converters.Add(new ExceptionConverter()); jsonOptions.Converters.Add(new MemoryStreamConverter()); jsonOptions.Converters.Add(new ConstantClassConverter()); + jsonOptions.Converters.Add(new DateOnlyConverter()); + jsonOptions.Converters.Add(new TimeOnlyConverter()); return jsonOptions; } diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsLogger.cs b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsLogger.cs index 265090aa9..707d4a8ee 100644 --- a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsLogger.cs +++ b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsLogger.cs @@ -466,6 +466,8 @@ private JsonSerializerOptions BuildJsonSerializerOptions() jsonOptions.Converters.Add(new ExceptionConverter()); jsonOptions.Converters.Add(new MemoryStreamConverter()); jsonOptions.Converters.Add(new ConstantClassConverter()); + jsonOptions.Converters.Add(new DateOnlyConverter()); + jsonOptions.Converters.Add(new TimeOnlyConverter()); jsonOptions.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; diff --git a/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerTest.cs b/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerTest.cs index 71db59320..c0fd3c09c 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerTest.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerTest.cs @@ -1219,5 +1219,87 @@ public void Log_Set_Execution_Environment_Context() $"{Constants.FeatureContextIdentifier}/Logger/{assemblyVersion}"); env.Received(1).GetEnvironmentVariable("AWS_EXECUTION_ENV"); } + + [Fact] + public void Log_Should_Serialize_DateOnly() + { + // Arrange + var loggerName = Guid.NewGuid().ToString(); + var service = Guid.NewGuid().ToString(); + var logLevel = LogLevel.Information; + var randomSampleRate = 0.5; + + var configurations = Substitute.For(); + configurations.Service.Returns(service); + configurations.LogLevel.Returns(logLevel.ToString()); + + var systemWrapper = Substitute.For(); + systemWrapper.GetRandom().Returns(randomSampleRate); + + var logger = new PowertoolsLogger(loggerName, configurations, systemWrapper, () => + new LoggerConfiguration + { + Service = null, + MinimumLevel = null, + LoggerOutputCase = LoggerOutputCase.CamelCase + }); + + var message = new + { + PropOne = "Value 1", + PropTwo = "Value 2", + Date = new DateOnly(2022, 1, 1) + }; + + logger.LogInformation(message); + + // Assert + systemWrapper.Received(1).LogLine( + Arg.Is(s => + s.Contains("\"message\":{\"propOne\":\"Value 1\",\"propTwo\":\"Value 2\",\"date\":\"2022-01-01\"}") + ) + ); + } + + [Fact] + public void Log_Should_Serialize_TimeOnly() + { + // Arrange + var loggerName = Guid.NewGuid().ToString(); + var service = Guid.NewGuid().ToString(); + var logLevel = LogLevel.Information; + var randomSampleRate = 0.5; + + var configurations = Substitute.For(); + configurations.Service.Returns(service); + configurations.LogLevel.Returns(logLevel.ToString()); + + var systemWrapper = Substitute.For(); + systemWrapper.GetRandom().Returns(randomSampleRate); + + var logger = new PowertoolsLogger(loggerName, configurations, systemWrapper, () => + new LoggerConfiguration + { + Service = null, + MinimumLevel = null, + LoggerOutputCase = LoggerOutputCase.CamelCase + }); + + var message = new + { + PropOne = "Value 1", + PropTwo = "Value 2", + Time = new TimeOnly(12, 0, 0) + }; + + logger.LogInformation(message); + + // Assert + systemWrapper.Received(1).LogLine( + Arg.Is(s => + s.Contains("\"message\":{\"propOne\":\"Value 1\",\"propTwo\":\"Value 2\",\"time\":\"12:00:00\"}") + ) + ); + } } } \ No newline at end of file From b3d666f5a7c023a4374519fecf535d88e876fc8d Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:56:08 +0100 Subject: [PATCH 2/3] fix code smell - dont need to be static --- .../Internal/LoggingAspectHandler.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingAspectHandler.cs b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingAspectHandler.cs index 441cdc222..38817e580 100644 --- a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingAspectHandler.cs +++ b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingAspectHandler.cs @@ -35,12 +35,12 @@ internal class LoggingAspectHandler : IMethodAspectHandler /// /// The is cold start /// - private static bool _isColdStart = true; + private bool _isColdStart = true; /// /// The initialize context /// - private static bool _initializeContext = true; + private bool _initializeContext = true; /// /// Clear state? @@ -388,8 +388,6 @@ private void LogEvent(object eventArg) /// internal static void ResetForTest() { - _isColdStart = true; - _initializeContext = true; PowertoolsLambdaContext.Clear(); Logger.LoggerProvider = null; Logger.RemoveAllKeys(); From f3ad52cea60d5923d28143fdbf80997476ee3f78 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Thu, 12 Oct 2023 10:16:07 +0100 Subject: [PATCH 3/3] make fields static --- .../Internal/LoggingAspectHandler.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingAspectHandler.cs b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingAspectHandler.cs index 38817e580..441cdc222 100644 --- a/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingAspectHandler.cs +++ b/libraries/src/AWS.Lambda.Powertools.Logging/Internal/LoggingAspectHandler.cs @@ -35,12 +35,12 @@ internal class LoggingAspectHandler : IMethodAspectHandler /// /// The is cold start /// - private bool _isColdStart = true; + private static bool _isColdStart = true; /// /// The initialize context /// - private bool _initializeContext = true; + private static bool _initializeContext = true; /// /// Clear state? @@ -388,6 +388,8 @@ private void LogEvent(object eventArg) /// internal static void ResetForTest() { + _isColdStart = true; + _initializeContext = true; PowertoolsLambdaContext.Clear(); Logger.LoggerProvider = null; Logger.RemoveAllKeys();