From 24c5fd6ab2f9a49bc71da9514dbdf92995c5614e Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Mon, 13 Mar 2023 20:48:29 +0300 Subject: [PATCH 1/3] Add AuditTo support --- ...nsoleAuditLoggerConfigurationExtensions.cs | 112 ++++++++++++++++++ ...AuditLoggerConfigurationExtensionsTests.cs | 64 ++++++++++ ...nsoleLoggerConfigurationExtensionsTests.cs | 1 + 3 files changed, 177 insertions(+) create mode 100644 src/Serilog.Sinks.Console/ConsoleAuditLoggerConfigurationExtensions.cs create mode 100644 test/Serilog.Sinks.Console.Tests/Configuration/ConsoleAuditLoggerConfigurationExtensionsTests.cs diff --git a/src/Serilog.Sinks.Console/ConsoleAuditLoggerConfigurationExtensions.cs b/src/Serilog.Sinks.Console/ConsoleAuditLoggerConfigurationExtensions.cs new file mode 100644 index 0000000..82ce5dd --- /dev/null +++ b/src/Serilog.Sinks.Console/ConsoleAuditLoggerConfigurationExtensions.cs @@ -0,0 +1,112 @@ +// Copyright 2017 Serilog Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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 Serilog.Configuration; +using Serilog.Core; +using Serilog.Events; +using Serilog.Formatting; +using Serilog.Sinks.SystemConsole; +using Serilog.Sinks.SystemConsole.Output; +using Serilog.Sinks.SystemConsole.Themes; +using System; + +namespace Serilog +{ + /// + /// Adds the AuditTo.Console() extension method to . + /// + public static class ConsoleAuditLoggerConfigurationExtensions + { + static readonly object DefaultSyncRoot = new object(); + const string DefaultConsoleOutputTemplate = "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"; + + /// + /// Writes log events to . + /// + /// Logger sink configuration. + /// The minimum level for + /// events passed through the sink. Ignored when is specified. + /// A message template describing the format used to write to the sink. + /// The default is "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}". + /// An object that will be used to `lock` (sync) access to the console output. If you specify this, you + /// will have the ability to lock on this object, and guarantee that the console sink will not be about to output anything while + /// the lock is held. + /// Supplies culture-specific formatting information, or null. + /// A switch allowing the pass-through minimum level + /// to be changed at runtime. + /// Specifies the level at which events will be written to standard error. + /// The theme to apply to the styled output. If not specified, + /// uses . + /// Applies the selected or default theme even when output redirection is detected. + /// Configuration object allowing method chaining. + /// When is null + /// When is null + public static LoggerConfiguration Console( + this LoggerAuditSinkConfiguration sinkConfiguration, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + string outputTemplate = DefaultConsoleOutputTemplate, + IFormatProvider? formatProvider = null, + LoggingLevelSwitch? levelSwitch = null, + LogEventLevel? standardErrorFromLevel = null, + ConsoleTheme? theme = null, + bool applyThemeToRedirectedOutput = false, + object? syncRoot = null) + { + if (sinkConfiguration is null) throw new ArgumentNullException(nameof(sinkConfiguration)); + if (outputTemplate is null) throw new ArgumentNullException(nameof(outputTemplate)); + + var appliedTheme = !applyThemeToRedirectedOutput && (System.Console.IsOutputRedirected || System.Console.IsErrorRedirected) ? + ConsoleTheme.None : + theme ?? SystemConsoleThemes.Literate; + + syncRoot ??= DefaultSyncRoot; + + var formatter = new OutputTemplateRenderer(appliedTheme, outputTemplate, formatProvider); + return sinkConfiguration.Sink(new ConsoleSink(appliedTheme, formatter, standardErrorFromLevel, syncRoot), restrictedToMinimumLevel, levelSwitch); + } + + /// + /// Writes log events to . + /// + /// Logger sink configuration. + /// Controls the rendering of log events into text, for example to log JSON. To + /// control plain text formatting, use the overload that accepts an output template. + /// An object that will be used to `lock` (sync) access to the console output. If you specify this, you + /// will have the ability to lock on this object, and guarantee that the console sink will not be about to output anything while + /// the lock is held. + /// The minimum level for + /// events passed through the sink. Ignored when is specified. + /// A switch allowing the pass-through minimum level + /// to be changed at runtime. + /// Specifies the level at which events will be written to standard error. + /// Configuration object allowing method chaining. + /// When is null + /// When is null + public static LoggerConfiguration Console( + this LoggerAuditSinkConfiguration sinkConfiguration, + ITextFormatter formatter, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch? levelSwitch = null, + LogEventLevel? standardErrorFromLevel = null, + object? syncRoot = null) + { + if (sinkConfiguration is null) throw new ArgumentNullException(nameof(sinkConfiguration)); + if (formatter is null) throw new ArgumentNullException(nameof(formatter)); + + syncRoot ??= DefaultSyncRoot; + + return sinkConfiguration.Sink(new ConsoleSink(ConsoleTheme.None, formatter, standardErrorFromLevel, syncRoot), restrictedToMinimumLevel, levelSwitch); + } + } +} diff --git a/test/Serilog.Sinks.Console.Tests/Configuration/ConsoleAuditLoggerConfigurationExtensionsTests.cs b/test/Serilog.Sinks.Console.Tests/Configuration/ConsoleAuditLoggerConfigurationExtensionsTests.cs new file mode 100644 index 0000000..1398128 --- /dev/null +++ b/test/Serilog.Sinks.Console.Tests/Configuration/ConsoleAuditLoggerConfigurationExtensionsTests.cs @@ -0,0 +1,64 @@ +using System; +using System.IO; +using System.Linq; +using Xunit; +using Serilog.Sinks.SystemConsole.Themes; + +namespace Serilog.Sinks.Console.Tests.Configuration +{ + [Collection("ConsoleSequentialTests")] + public class ConsoleAuditLoggerConfigurationExtensionsTests + { + [Fact] + public void OutputFormattingIsIgnored() + { + using (var stream = new MemoryStream()) + { + var sw = new StreamWriter(stream); + + System.Console.SetOut(sw); + var config = new LoggerConfiguration() + .AuditTo.Console(theme: AnsiConsoleTheme.Literate, + applyThemeToRedirectedOutput: false); + + var logger = config.CreateLogger(); + + logger.Error("test"); + stream.Position = 0; + + using (var streamReader = new StreamReader(stream)) + { + var result = streamReader.ReadToEnd(); + var controlCharacterCount = result.Count(c => Char.IsControl(c) && !Char.IsWhiteSpace(c)); + Assert.Equal(0, controlCharacterCount); + } + } + } + + [Fact] + public void OutputFormattingIsPresent() + { + using (var stream = new MemoryStream()) + { + var sw = new StreamWriter(stream); + + System.Console.SetOut(sw); + var config = new LoggerConfiguration() + .AuditTo.Console(theme: AnsiConsoleTheme.Literate, + applyThemeToRedirectedOutput: true); + + var logger = config.CreateLogger(); + + logger.Error("test"); + stream.Position = 0; + + using (var streamReader = new StreamReader(stream)) + { + var result = streamReader.ReadToEnd(); + var controlCharacterCount = result.Count(c => Char.IsControl(c) && !Char.IsWhiteSpace(c)); + Assert.NotEqual(0, controlCharacterCount); + } + } + } + } +} \ No newline at end of file diff --git a/test/Serilog.Sinks.Console.Tests/Configuration/ConsoleLoggerConfigurationExtensionsTests.cs b/test/Serilog.Sinks.Console.Tests/Configuration/ConsoleLoggerConfigurationExtensionsTests.cs index 74596c5..a66c82f 100644 --- a/test/Serilog.Sinks.Console.Tests/Configuration/ConsoleLoggerConfigurationExtensionsTests.cs +++ b/test/Serilog.Sinks.Console.Tests/Configuration/ConsoleLoggerConfigurationExtensionsTests.cs @@ -6,6 +6,7 @@ namespace Serilog.Sinks.Console.Tests.Configuration { + [Collection("ConsoleSequentialTests")] public class ConsoleLoggerConfigurationExtensionsTests { [Fact] From 4fd69e15959edf08e7aa1b4b343ad498d6193c28 Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Thu, 20 Jul 2023 09:42:30 +0300 Subject: [PATCH 2/3] review --- .../ConsoleAuditLoggerConfigurationExtensions.cs | 11 ++++------- .../ConsoleLoggerConfigurationExtensions.cs | 4 ++-- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Serilog.Sinks.Console/ConsoleAuditLoggerConfigurationExtensions.cs b/src/Serilog.Sinks.Console/ConsoleAuditLoggerConfigurationExtensions.cs index 82ce5dd..6073c37 100644 --- a/src/Serilog.Sinks.Console/ConsoleAuditLoggerConfigurationExtensions.cs +++ b/src/Serilog.Sinks.Console/ConsoleAuditLoggerConfigurationExtensions.cs @@ -28,9 +28,6 @@ namespace Serilog /// public static class ConsoleAuditLoggerConfigurationExtensions { - static readonly object DefaultSyncRoot = new object(); - const string DefaultConsoleOutputTemplate = "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"; - /// /// Writes log events to . /// @@ -55,11 +52,11 @@ public static class ConsoleAuditLoggerConfigurationExtensions public static LoggerConfiguration Console( this LoggerAuditSinkConfiguration sinkConfiguration, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - string outputTemplate = DefaultConsoleOutputTemplate, + string outputTemplate = ConsoleLoggerConfigurationExtensions.DefaultConsoleOutputTemplate, IFormatProvider? formatProvider = null, LoggingLevelSwitch? levelSwitch = null, LogEventLevel? standardErrorFromLevel = null, - ConsoleTheme? theme = null, + ConsoleTheme? theme = null, bool applyThemeToRedirectedOutput = false, object? syncRoot = null) { @@ -70,7 +67,7 @@ public static LoggerConfiguration Console( ConsoleTheme.None : theme ?? SystemConsoleThemes.Literate; - syncRoot ??= DefaultSyncRoot; + syncRoot ??= ConsoleLoggerConfigurationExtensions.DefaultSyncRoot; var formatter = new OutputTemplateRenderer(appliedTheme, outputTemplate, formatProvider); return sinkConfiguration.Sink(new ConsoleSink(appliedTheme, formatter, standardErrorFromLevel, syncRoot), restrictedToMinimumLevel, levelSwitch); @@ -104,7 +101,7 @@ public static LoggerConfiguration Console( if (sinkConfiguration is null) throw new ArgumentNullException(nameof(sinkConfiguration)); if (formatter is null) throw new ArgumentNullException(nameof(formatter)); - syncRoot ??= DefaultSyncRoot; + syncRoot ??= ConsoleLoggerConfigurationExtensions.DefaultSyncRoot; return sinkConfiguration.Sink(new ConsoleSink(ConsoleTheme.None, formatter, standardErrorFromLevel, syncRoot), restrictedToMinimumLevel, levelSwitch); } diff --git a/src/Serilog.Sinks.Console/ConsoleLoggerConfigurationExtensions.cs b/src/Serilog.Sinks.Console/ConsoleLoggerConfigurationExtensions.cs index e55f7a2..cca312a 100644 --- a/src/Serilog.Sinks.Console/ConsoleLoggerConfigurationExtensions.cs +++ b/src/Serilog.Sinks.Console/ConsoleLoggerConfigurationExtensions.cs @@ -28,8 +28,8 @@ namespace Serilog /// public static class ConsoleLoggerConfigurationExtensions { - static readonly object DefaultSyncRoot = new object(); - const string DefaultConsoleOutputTemplate = "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"; + internal static readonly object DefaultSyncRoot = new object(); + internal const string DefaultConsoleOutputTemplate = "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"; /// /// Writes log events to . From 7453ac296e480b61815a4b7840867d9cc84b2c7a Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Fri, 21 Jul 2023 22:42:04 +0300 Subject: [PATCH 3/3] fix --- .../Approval/Serilog.Sinks.Console.approved.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/Serilog.Sinks.Console.Tests/Approval/Serilog.Sinks.Console.approved.txt b/test/Serilog.Sinks.Console.Tests/Approval/Serilog.Sinks.Console.approved.txt index 25fdc2e..a6239fb 100644 --- a/test/Serilog.Sinks.Console.Tests/Approval/Serilog.Sinks.Console.approved.txt +++ b/test/Serilog.Sinks.Console.Tests/Approval/Serilog.Sinks.Console.approved.txt @@ -1,5 +1,10 @@ namespace Serilog { + public static class ConsoleAuditLoggerConfigurationExtensions + { + public static Serilog.LoggerConfiguration Console(this Serilog.Configuration.LoggerAuditSinkConfiguration sinkConfiguration, Serilog.Formatting.ITextFormatter formatter, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null, Serilog.Events.LogEventLevel? standardErrorFromLevel = default, object? syncRoot = null) { } + public static Serilog.LoggerConfiguration Console(this Serilog.Configuration.LoggerAuditSinkConfiguration sinkConfiguration, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, string outputTemplate = "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}", System.IFormatProvider? formatProvider = null, Serilog.Core.LoggingLevelSwitch? levelSwitch = null, Serilog.Events.LogEventLevel? standardErrorFromLevel = default, Serilog.Sinks.SystemConsole.Themes.ConsoleTheme? theme = null, bool applyThemeToRedirectedOutput = false, object? syncRoot = null) { } + } public static class ConsoleLoggerConfigurationExtensions { public static Serilog.LoggerConfiguration Console(this Serilog.Configuration.LoggerSinkConfiguration sinkConfiguration, Serilog.Formatting.ITextFormatter formatter, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null, Serilog.Events.LogEventLevel? standardErrorFromLevel = default, object? syncRoot = null) { }