From e5ae02f6ed7817c49392deafe8b77385125bbe6d Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Tue, 6 Sep 2022 10:57:40 +1000 Subject: [PATCH 01/14] Dev version bump [skip ci] --- src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj b/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj index 06abb29..2631dc2 100644 --- a/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj +++ b/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj @@ -2,7 +2,7 @@ A Serilog sink that writes log events to the console/terminal. - 4.1.0 + 4.1.1 Serilog Contributors net45;netstandard1.3;netstandard2.0;net5.0 8.0 From 7356b35ad3c01710393bfc99a6cde8d7c81e5a8d Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 27 Sep 2022 10:25:30 +1000 Subject: [PATCH 02/14] update Microsoft.NET.Test.Sdk to 16.6.1 --- .../Serilog.Sinks.Console.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Serilog.Sinks.Console.Tests/Serilog.Sinks.Console.Tests.csproj b/test/Serilog.Sinks.Console.Tests/Serilog.Sinks.Console.Tests.csproj index 5e141e3..e7717cc 100644 --- a/test/Serilog.Sinks.Console.Tests/Serilog.Sinks.Console.Tests.csproj +++ b/test/Serilog.Sinks.Console.Tests/Serilog.Sinks.Console.Tests.csproj @@ -15,7 +15,7 @@ - + From ab5fdb80f22b4dbe88db28e0017a066134104ad0 Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Sat, 3 Dec 2022 02:12:07 +0300 Subject: [PATCH 03/14] Optimize TimestampTokenRenderer --- .../Output/TimestampTokenRenderer.cs | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/TimestampTokenRenderer.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/TimestampTokenRenderer.cs index c763279..8480829 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/TimestampTokenRenderer.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/TimestampTokenRenderer.cs @@ -13,6 +13,7 @@ // limitations under the License. using System; +using System.Globalization; using System.IO; using Serilog.Events; using Serilog.Parsing; @@ -36,9 +37,7 @@ public TimestampTokenRenderer(ConsoleTheme theme, PropertyToken token, IFormatPr public override void Render(LogEvent logEvent, TextWriter output) { - // We need access to ScalarValue.Render() to avoid this alloc; just ensures - // that custom format providers are supported properly. - var sv = new ScalarValue(logEvent.Timestamp); + var sv = new DateTimeOffsetValue(logEvent.Timestamp); var _ = 0; using (_theme.Apply(output, ConsoleThemeStyle.SecondaryText, ref _)) @@ -56,5 +55,35 @@ public override void Render(LogEvent logEvent, TextWriter output) } } } + + readonly struct DateTimeOffsetValue + { + public DateTimeOffsetValue(DateTimeOffset value) + { + Value = value; + } + + public DateTimeOffset Value { get; } + + public void Render(TextWriter output, string? format = null, IFormatProvider? formatProvider = null) + { + var custom = (ICustomFormatter?)formatProvider?.GetFormat(typeof(ICustomFormatter)); + if (custom != null) + { + output.Write(custom.Format(format, Value, formatProvider)); + return; + } + +#if NET5_0_OR_GREATER + Span buffer = stackalloc char[32]; + if (Value.TryFormat(buffer, out int written, format, formatProvider ?? CultureInfo.InvariantCulture)) + output.Write(buffer.Slice(0, written)); + else + output.Write(Value.ToString(format, formatProvider ?? CultureInfo.InvariantCulture)); +#else + output.Write(Value.ToString(format, formatProvider ?? CultureInfo.InvariantCulture)); +#endif + } + } } } From 212ad03d030327a81511a629f2b3fb4be5753ca5 Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Thu, 5 Jan 2023 08:35:24 +0300 Subject: [PATCH 04/14] FEATURE_SPAN --- src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj | 4 ++++ .../Sinks/SystemConsole/Output/TimestampTokenRenderer.cs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj b/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj index 2631dc2..5ec0ccd 100644 --- a/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj +++ b/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj @@ -26,6 +26,10 @@ $(DefineConstants);RUNTIME_INFORMATION + + $(DefineConstants);FEATURE_SPAN + + diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/TimestampTokenRenderer.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/TimestampTokenRenderer.cs index 8480829..bc5f000 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/TimestampTokenRenderer.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/TimestampTokenRenderer.cs @@ -74,7 +74,7 @@ public void Render(TextWriter output, string? format = null, IFormatProvider? fo return; } -#if NET5_0_OR_GREATER +#if FEATURE_SPAN Span buffer = stackalloc char[32]; if (Value.TryFormat(buffer, out int written, format, formatProvider ?? CultureInfo.InvariantCulture)) output.Write(buffer.Slice(0, written)); From 106c377a8f9f0ccedab309ede481ca10a12ee5c0 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Sat, 14 Jan 2023 08:34:54 +1000 Subject: [PATCH 05/14] Use Serilog's level moniker function --- .../Serilog.Sinks.Console.csproj | 1 + .../SystemConsole/Output/LevelOutputFormat.cs | 149 +++++++++--------- .../Output/OutputTemplateRendererTests.cs | 10 ++ 3 files changed, 86 insertions(+), 74 deletions(-) diff --git a/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj b/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj index 5ec0ccd..d48dc58 100644 --- a/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj +++ b/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj @@ -20,6 +20,7 @@ True Serilog + latest diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/LevelOutputFormat.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/LevelOutputFormat.cs index d703bb8..a0c996f 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/LevelOutputFormat.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/LevelOutputFormat.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Serilog Contributors +// Copyright 2023 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,89 +12,90 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System; using Serilog.Events; using Serilog.Sinks.SystemConsole.Rendering; -namespace Serilog.Sinks.SystemConsole.Output -{ - /// - /// Implements the {Level} element. - /// can now have a fixed width applied to it, as well as casing rules. - /// Width is set through formats like "u3" (uppercase three chars), - /// "w1" (one lowercase char), or "t4" (title case four chars). - /// - static class LevelOutputFormat - { - static readonly string[][] TitleCaseLevelMap = - { - new[] { "V", "Vb", "Vrb", "Verb" }, - new[] { "D", "De", "Dbg", "Dbug" }, - new[] { "I", "In", "Inf", "Info" }, - new[] { "W", "Wn", "Wrn", "Warn" }, - new[] { "E", "Er", "Err", "Eror" }, - new[] { "F", "Fa", "Ftl", "Fatl" }, - }; +namespace Serilog.Sinks.SystemConsole.Output; - static readonly string[][] LowercaseLevelMap = - { - new[] { "v", "vb", "vrb", "verb" }, - new[] { "d", "de", "dbg", "dbug" }, - new[] { "i", "in", "inf", "info" }, - new[] { "w", "wn", "wrn", "warn" }, - new[] { "e", "er", "err", "eror" }, - new[] { "f", "fa", "ftl", "fatl" }, - }; +/// +/// Implements the {Level} element. +/// can now have a fixed width applied to it, as well as casing rules. +/// Width is set through formats like "u3" (uppercase three chars), +/// "w1" (one lowercase char), or "t4" (title case four chars). +/// +static class LevelOutputFormat +{ + static readonly string[][] TitleCaseLevelMap = { + new []{ "V", "Vb", "Vrb", "Verb", "Verbo", "Verbos", "Verbose" }, + new []{ "D", "De", "Dbg", "Dbug", "Debug" }, + new []{ "I", "In", "Inf", "Info", "Infor", "Inform", "Informa", "Informat", "Informati", "Informatio", "Information" }, + new []{ "W", "Wn", "Wrn", "Warn", "Warni", "Warnin", "Warning" }, + new []{ "E", "Er", "Err", "Eror", "Error" }, + new []{ "F", "Fa", "Ftl", "Fatl", "Fatal" } + }; - static readonly string[][] UppercaseLevelMap = - { - new[] { "V", "VB", "VRB", "VERB" }, - new[] { "D", "DE", "DBG", "DBUG" }, - new[] { "I", "IN", "INF", "INFO" }, - new[] { "W", "WN", "WRN", "WARN" }, - new[] { "E", "ER", "ERR", "EROR" }, - new[] { "F", "FA", "FTL", "FATL" }, - }; + static readonly string[][] LowerCaseLevelMap = { + new []{ "v", "vb", "vrb", "verb", "verbo", "verbos", "verbose" }, + new []{ "d", "de", "dbg", "dbug", "debug" }, + new []{ "i", "in", "inf", "info", "infor", "inform", "informa", "informat", "informati", "informatio", "information" }, + new []{ "w", "wn", "wrn", "warn", "warni", "warnin", "warning" }, + new []{ "e", "er", "err", "eror", "error" }, + new []{ "f", "fa", "ftl", "fatl", "fatal" } + }; - public static string GetLevelMoniker(LogEventLevel value, string? format = null) - { - if (format is null || format.Length != 2 && format.Length != 3) - return Casing.Format(value.ToString(), format); + static readonly string[][] UpperCaseLevelMap = { + new []{ "V", "VB", "VRB", "VERB", "VERBO", "VERBOS", "VERBOSE" }, + new []{ "D", "DE", "DBG", "DBUG", "DEBUG" }, + new []{ "I", "IN", "INF", "INFO", "INFOR", "INFORM", "INFORMA", "INFORMAT", "INFORMATI", "INFORMATIO", "INFORMATION" }, + new []{ "W", "WN", "WRN", "WARN", "WARNI", "WARNIN", "WARNING" }, + new []{ "E", "ER", "ERR", "EROR", "ERROR" }, + new []{ "F", "FA", "FTL", "FATL", "FATAL" } + }; - // Using int.Parse() here requires allocating a string to exclude the first character prefix. - // Junk like "wxy" will be accepted but produce benign results. - var width = format[1] - '0'; - if (format.Length == 3) - { - width *= 10; - width += format[2] - '0'; - } + public static string GetLevelMoniker(LogEventLevel value, string? format = null) + { + var index = (int)value; + if (index is < 0 or > (int)LogEventLevel.Fatal) + return Casing.Format(value.ToString(), format); - if (width < 1) - return string.Empty; + if (format == null || format.Length != 2 && format.Length != 3) + return Casing.Format(GetLevelMoniker(TitleCaseLevelMap, index), format); - if (width > 4) - { - var stringValue = value.ToString(); - if (stringValue.Length > width) - stringValue = stringValue.Substring(0, width); - return Casing.Format(stringValue); - } + // Using int.Parse() here requires allocating a string to exclude the first character prefix. + // Junk like "wxy" will be accepted but produce benign results. + var width = format[1] - '0'; + if (format.Length == 3) + { + width *= 10; + width += format[2] - '0'; + } - var index = (int)value; - if (index >= 0 && index <= (int)LogEventLevel.Fatal) - { - switch (format[0]) - { - case 'w': - return LowercaseLevelMap[index][width - 1]; - case 'u': - return UppercaseLevelMap[index][width - 1]; - case 't': - return TitleCaseLevelMap[index][width - 1]; - } - } + if (width < 1) + return string.Empty; - return Casing.Format(value.ToString(), format); + switch (format[0]) + { + case 'w': + return GetLevelMoniker(LowerCaseLevelMap, index, width); + case 'u': + return GetLevelMoniker(UpperCaseLevelMap, index, width); + case 't': + return GetLevelMoniker(TitleCaseLevelMap, index, width); + default: + return Casing.Format(GetLevelMoniker(TitleCaseLevelMap, index), format); } } -} \ No newline at end of file + + static string GetLevelMoniker(string[][] caseLevelMap, int index, int width) + { + var caseLevel = caseLevelMap[index]; + return caseLevel[Math.Min(width, caseLevel.Length) - 1]; + } + + static string GetLevelMoniker(string[][] caseLevelMap, int index) + { + var caseLevel = caseLevelMap[index]; + return caseLevel[caseLevel.Length - 1]; + } +} diff --git a/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs b/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs index 1a71415..9057d54 100644 --- a/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs +++ b/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs @@ -131,6 +131,16 @@ public void FixedLengthLevelSupportsLowerCasing() formatter.Format(evt, sw); Assert.Equal("inf", sw.ToString()); } + + [Fact] + public void FixedLengthLevelSupportsCasingForWideNames() + { + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Level:w6}", CultureInfo.InvariantCulture); + var evt = DelegatingSink.GetLogEvent(l => l.Information("Hello")); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal("inform", sw.ToString()); + } [Fact] public void DefaultLevelLengthIsFullText() From 6613b2b71567928b3e38b25e6f6c46a290051407 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 3 Feb 2023 13:56:58 +1000 Subject: [PATCH 06/14] Copy added tests from #135 --- .../Output/OutputTemplateRendererTests.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs b/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs index 9057d54..793356b 100644 --- a/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs +++ b/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs @@ -105,11 +105,27 @@ public void FixedLengthLevelIsSupported( int width, string expected) { - var formatter = new OutputTemplateRenderer(ConsoleTheme.None, $"{{Level:t{width}}}", CultureInfo.InvariantCulture); + var formatter1 = new OutputTemplateRenderer(ConsoleTheme.None, $"{{Level:t{width}}}", CultureInfo.InvariantCulture); var evt = DelegatingSink.GetLogEvent(l => l.Write(level, "Hello")); + var evt1 = DelegatingSink.GetLogEvent(l => l.Write(level, "Hello")); var sw = new StringWriter(); + var sw1 = new StringWriter(); formatter.Format(evt, sw); + formatter1.Format(evt1, sw1); Assert.Equal(expected, sw.ToString()); + Assert.Equal(expected, sw1.ToString()); + + var formatter2 = new OutputTemplateRenderer(ConsoleTheme.None, $"{{Level:u{width}}}", CultureInfo.InvariantCulture); + var evt2 = DelegatingSink.GetLogEvent(l => l.Write(level, "Hello")); + var sw2 = new StringWriter(); + formatter2.Format(evt2, sw2); + Assert.Equal(expected.ToUpper(), sw2.ToString()); + + var formatter3 = new OutputTemplateRenderer(ConsoleTheme.None, $"{{Level:w{width}}}", CultureInfo.InvariantCulture); + var evt3 = DelegatingSink.GetLogEvent(l => l.Write(level, "Hello")); + var sw3 = new StringWriter(); + formatter3.Format(evt3, sw3); + Assert.Equal(expected.ToLower(), sw3.ToString()); } [Fact] From 834cd89f03c0675f9f5996e901fe03ab0ba73fad Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 3 Feb 2023 14:02:13 +1000 Subject: [PATCH 07/14] Fix bad copy/paste :-) --- .../Output/OutputTemplateRendererTests.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs b/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs index 793356b..9dd5d5f 100644 --- a/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs +++ b/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs @@ -106,13 +106,9 @@ public void FixedLengthLevelIsSupported( string expected) { var formatter1 = new OutputTemplateRenderer(ConsoleTheme.None, $"{{Level:t{width}}}", CultureInfo.InvariantCulture); - var evt = DelegatingSink.GetLogEvent(l => l.Write(level, "Hello")); var evt1 = DelegatingSink.GetLogEvent(l => l.Write(level, "Hello")); - var sw = new StringWriter(); var sw1 = new StringWriter(); - formatter.Format(evt, sw); formatter1.Format(evt1, sw1); - Assert.Equal(expected, sw.ToString()); Assert.Equal(expected, sw1.ToString()); var formatter2 = new OutputTemplateRenderer(ConsoleTheme.None, $"{{Level:u{width}}}", CultureInfo.InvariantCulture); From 24c5fd6ab2f9a49bc71da9514dbdf92995c5614e Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Mon, 13 Mar 2023 20:48:29 +0300 Subject: [PATCH 08/14] 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 0391d84c46df94080f9d8cf672ef67ea37a9639b Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Mon, 13 Mar 2023 20:58:40 +0300 Subject: [PATCH 09/14] Add API approval test --- .../Approval/ApiApprovalTests.cs | 27 +++++++ .../Serilog.Sinks.Console.approved.txt | 70 +++++++++++++++++++ .../Serilog.Sinks.Console.Tests.csproj | 4 +- 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 test/Serilog.Sinks.Console.Tests/Approval/ApiApprovalTests.cs create mode 100644 test/Serilog.Sinks.Console.Tests/Approval/Serilog.Sinks.Console.approved.txt diff --git a/test/Serilog.Sinks.Console.Tests/Approval/ApiApprovalTests.cs b/test/Serilog.Sinks.Console.Tests/Approval/ApiApprovalTests.cs new file mode 100644 index 0000000..46b1c72 --- /dev/null +++ b/test/Serilog.Sinks.Console.Tests/Approval/ApiApprovalTests.cs @@ -0,0 +1,27 @@ +#if NET5_0 + +using PublicApiGenerator; +using Shouldly; +using Xunit; + +namespace Serilog.Sinks.Console.Tests.Approval +{ + public class ApiApprovalTests + { + [Fact] + public void PublicApi_Should_Not_Change_Unintentionally() + { + var assembly = typeof(ConsoleLoggerConfigurationExtensions).Assembly; + var publicApi = assembly.GeneratePublicApi( + new ApiGeneratorOptions() + { + IncludeAssemblyAttributes = false, + ExcludeAttributes = new[] { "System.Diagnostics.DebuggerDisplayAttribute" }, + }); + + publicApi.ShouldMatchApproved(options => options.WithFilenameGenerator((_, __, fileType, fileExtension) => $"{assembly.GetName().Name!}.{fileType}.{fileExtension}")); + } + } +} + +#endif 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 new file mode 100644 index 0000000..25fdc2e --- /dev/null +++ b/test/Serilog.Sinks.Console.Tests/Approval/Serilog.Sinks.Console.approved.txt @@ -0,0 +1,70 @@ +namespace Serilog +{ + 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) { } + public static Serilog.LoggerConfiguration Console(this Serilog.Configuration.LoggerSinkConfiguration 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) { } + } +} +namespace Serilog.Sinks.SystemConsole.Themes +{ + public class AnsiConsoleTheme : Serilog.Sinks.SystemConsole.Themes.ConsoleTheme + { + public AnsiConsoleTheme(System.Collections.Generic.IReadOnlyDictionary styles) { } + public override bool CanBuffer { get; } + protected override int ResetCharCount { get; } + public static Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme Code { get; } + public static Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme Grayscale { get; } + public static Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme Literate { get; } + public static Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme Sixteen { get; } + public override void Reset(System.IO.TextWriter output) { } + public override int Set(System.IO.TextWriter output, Serilog.Sinks.SystemConsole.Themes.ConsoleThemeStyle style) { } + } + public abstract class ConsoleTheme + { + protected ConsoleTheme() { } + public abstract bool CanBuffer { get; } + protected abstract int ResetCharCount { get; } + public static Serilog.Sinks.SystemConsole.Themes.ConsoleTheme None { get; } + public abstract void Reset(System.IO.TextWriter output); + public abstract int Set(System.IO.TextWriter output, Serilog.Sinks.SystemConsole.Themes.ConsoleThemeStyle style); + } + public enum ConsoleThemeStyle + { + Text = 0, + SecondaryText = 1, + TertiaryText = 2, + Invalid = 3, + Null = 4, + Name = 5, + String = 6, + Number = 7, + Boolean = 8, + Scalar = 9, + [System.Obsolete("Use ConsoleThemeStyle.Scalar instead")] + Object = 9, + LevelVerbose = 10, + LevelDebug = 11, + LevelInformation = 12, + LevelWarning = 13, + LevelError = 14, + LevelFatal = 15, + } + public class SystemConsoleTheme : Serilog.Sinks.SystemConsole.Themes.ConsoleTheme + { + public SystemConsoleTheme(System.Collections.Generic.IReadOnlyDictionary styles) { } + public override bool CanBuffer { get; } + protected override int ResetCharCount { get; } + public System.Collections.Generic.IReadOnlyDictionary Styles { get; } + public static Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme Colored { get; } + public static Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme Grayscale { get; } + public static Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme Literate { get; } + public override void Reset(System.IO.TextWriter output) { } + public override int Set(System.IO.TextWriter output, Serilog.Sinks.SystemConsole.Themes.ConsoleThemeStyle style) { } + } + public struct SystemConsoleThemeStyle + { + public System.ConsoleColor? Background; + public System.ConsoleColor? Foreground; + } +} \ No newline at end of file diff --git a/test/Serilog.Sinks.Console.Tests/Serilog.Sinks.Console.Tests.csproj b/test/Serilog.Sinks.Console.Tests/Serilog.Sinks.Console.Tests.csproj index e7717cc..57b8025 100644 --- a/test/Serilog.Sinks.Console.Tests/Serilog.Sinks.Console.Tests.csproj +++ b/test/Serilog.Sinks.Console.Tests/Serilog.Sinks.Console.Tests.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1;netcoreapp2.2;netcoreapp3.0;netcoreapp3.1;net452;net462;net472;net48;net5.0 @@ -19,6 +19,8 @@ + + From 4fd69e15959edf08e7aa1b4b343ad498d6193c28 Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Thu, 20 Jul 2023 09:42:30 +0300 Subject: [PATCH 10/14] 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 11/14] 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) { } From da9c6ca8163b857b1e57ccc977fe3384aceb4faf Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Sat, 22 Jul 2023 08:29:44 +1000 Subject: [PATCH 12/14] Minor version bump - auditing support added --- src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj b/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj index d48dc58..a15a82c 100644 --- a/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj +++ b/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj @@ -2,7 +2,7 @@ A Serilog sink that writes log events to the console/terminal. - 4.1.1 + 4.2.0 Serilog Contributors net45;netstandard1.3;netstandard2.0;net5.0 8.0 From 0f8ccf2b9c27fa38dccfb8e7b07a79de1769ec5e Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 11 Oct 2023 18:47:04 +1000 Subject: [PATCH 13/14] Support TraceId and SpanId output template tokens --- sample/ConsoleDemo/ConsoleDemo.csproj | 2 +- sample/SyncWritesDemo/SyncWritesDemo.csproj | 2 +- .../Serilog.Sinks.Console.csproj | 80 ++++++++++--------- .../Output/NewLineTokenRenderer.cs | 2 +- .../Output/OutputTemplateRenderer.cs | 10 ++- .../Output/SpanIdTokenRenderer.cs | 34 ++++++++ .../Output/TraceIdTokenRenderer.cs | 34 ++++++++ .../Output/OutputTemplateRendererTests.cs | 28 ++++++- .../Serilog.Sinks.Console.Tests.csproj | 3 +- 9 files changed, 152 insertions(+), 43 deletions(-) create mode 100644 src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/SpanIdTokenRenderer.cs create mode 100644 src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/TraceIdTokenRenderer.cs diff --git a/sample/ConsoleDemo/ConsoleDemo.csproj b/sample/ConsoleDemo/ConsoleDemo.csproj index 81b1a1f..de9024a 100644 --- a/sample/ConsoleDemo/ConsoleDemo.csproj +++ b/sample/ConsoleDemo/ConsoleDemo.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.1;netcoreapp2.2;netcoreapp3.0;netcoreapp3.1;net452;net462;net472;net48;net5.0 + net7.0 diff --git a/sample/SyncWritesDemo/SyncWritesDemo.csproj b/sample/SyncWritesDemo/SyncWritesDemo.csproj index a72595e..de9024a 100644 --- a/sample/SyncWritesDemo/SyncWritesDemo.csproj +++ b/sample/SyncWritesDemo/SyncWritesDemo.csproj @@ -2,7 +2,7 @@ Exe - net5.0 + net7.0 diff --git a/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj b/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj index a15a82c..c1aeaf1 100644 --- a/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj +++ b/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj @@ -1,42 +1,50 @@ + + A Serilog sink that writes log events to the console/terminal. + 5.0.0 + Serilog Contributors + net462;net471 + $(TargetFrameworks);netstandard2.1;netstandard2.0;net5.0;net6.0;net7.0 + enable + ../../assets/Serilog.snk + true + true + serilog;console;terminal + icon.png + https://github.com/serilog/serilog-sinks-console + Apache-2.0 + https://github.com/serilog/serilog-sinks-console + git + true + True + Serilog + latest + README.md + - - A Serilog sink that writes log events to the console/terminal. - 4.2.0 - Serilog Contributors - net45;netstandard1.3;netstandard2.0;net5.0 - 8.0 - enable - ../../assets/Serilog.snk - true - true - serilog;console;terminal - icon.png - https://github.com/serilog/serilog-sinks-console - Apache-2.0 - https://github.com/serilog/serilog-sinks-console - git - true - True - - Serilog - latest - + + $(DefineConstants);RUNTIME_INFORMATION + - - $(DefineConstants);RUNTIME_INFORMATION - - - - $(DefineConstants);FEATURE_SPAN - + + $(DefineConstants);FEATURE_SPAN + + + + $(DefineConstants);FEATURE_SPAN + + + + $(DefineConstants);FEATURE_SPAN + - - - - + + + + - - - + + + + diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/NewLineTokenRenderer.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/NewLineTokenRenderer.cs index c7174e8..4b072c3 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/NewLineTokenRenderer.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/NewLineTokenRenderer.cs @@ -37,4 +37,4 @@ public override void Render(LogEvent logEvent, TextWriter output) output.WriteLine(); } } -} \ No newline at end of file +} diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/OutputTemplateRenderer.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/OutputTemplateRenderer.cs index c62ab67..f922290 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/OutputTemplateRenderer.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/OutputTemplateRenderer.cs @@ -50,6 +50,14 @@ public OutputTemplateRenderer(ConsoleTheme theme, string outputTemplate, IFormat { renderers.Add(new NewLineTokenRenderer(pt.Alignment)); } + else if (pt.PropertyName == OutputProperties.TraceIdPropertyName) + { + renderers.Add(new TraceIdTokenRenderer(theme, pt)); + } + else if (pt.PropertyName == OutputProperties.SpanIdPropertyName) + { + renderers.Add(new SpanIdTokenRenderer(theme, pt)); + } else if (pt.PropertyName == OutputProperties.ExceptionPropertyName) { renderers.Add(new ExceptionTokenRenderer(theme, pt)); @@ -62,7 +70,7 @@ public OutputTemplateRenderer(ConsoleTheme theme, string outputTemplate, IFormat { renderers.Add(new TimestampTokenRenderer(theme, pt, formatProvider)); } - else if (pt.PropertyName == "Properties") + else if (pt.PropertyName == OutputProperties.PropertiesPropertyName) { renderers.Add(new PropertiesTokenRenderer(theme, pt, template, formatProvider)); } diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/SpanIdTokenRenderer.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/SpanIdTokenRenderer.cs new file mode 100644 index 0000000..394a7da --- /dev/null +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/SpanIdTokenRenderer.cs @@ -0,0 +1,34 @@ +using System.IO; +using Serilog.Events; +using Serilog.Parsing; +using Serilog.Sinks.SystemConsole.Rendering; +using Serilog.Sinks.SystemConsole.Themes; + +namespace Serilog.Sinks.SystemConsole.Output; + +class SpanIdTokenRenderer : OutputTemplateTokenRenderer +{ + readonly ConsoleTheme _theme; + readonly Alignment? _alignment; + + public SpanIdTokenRenderer(ConsoleTheme theme, PropertyToken spanIdToken) + { + _theme = theme; + _alignment = spanIdToken.Alignment; + } + + public override void Render(LogEvent logEvent, TextWriter output) + { + if (logEvent.SpanId is not { } spanId) + return; + + var _ = 0; + using (_theme.Apply(output, ConsoleThemeStyle.Text, ref _)) + { + if (_alignment is {} alignment) + Padding.Apply(output, spanId.ToString(), alignment); + else + output.Write(spanId); + } + } +} \ No newline at end of file diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/TraceIdTokenRenderer.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/TraceIdTokenRenderer.cs new file mode 100644 index 0000000..c2be4d0 --- /dev/null +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/TraceIdTokenRenderer.cs @@ -0,0 +1,34 @@ +using System.IO; +using Serilog.Events; +using Serilog.Parsing; +using Serilog.Sinks.SystemConsole.Rendering; +using Serilog.Sinks.SystemConsole.Themes; + +namespace Serilog.Sinks.SystemConsole.Output; + +class TraceIdTokenRenderer : OutputTemplateTokenRenderer +{ + readonly ConsoleTheme _theme; + readonly Alignment? _alignment; + + public TraceIdTokenRenderer(ConsoleTheme theme, PropertyToken traceIdToken) + { + _theme = theme; + _alignment = traceIdToken.Alignment; + } + + public override void Render(LogEvent logEvent, TextWriter output) + { + if (logEvent.TraceId is not { } traceId) + return; + + var _ = 0; + using (_theme.Apply(output, ConsoleThemeStyle.Text, ref _)) + { + if (_alignment is {} alignment) + Padding.Apply(output, traceId.ToString(), alignment); + else + output.Write(traceId); + } + } +} \ No newline at end of file diff --git a/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs b/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs index 9dd5d5f..59bbedc 100644 --- a/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs +++ b/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs @@ -1,8 +1,10 @@ using System; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using Serilog.Events; +using Serilog.Parsing; using Serilog.Sinks.Console.Tests.Support; using Serilog.Sinks.SystemConsole.Output; using Serilog.Sinks.SystemConsole.Themes; @@ -188,7 +190,7 @@ public SizeFormatter(IFormatProvider innerFormatProvider) _innerFormatProvider = innerFormatProvider; } - public object? GetFormat(Type? formatType) + public object GetFormat(Type? formatType) { return formatType == typeof(ICustomFormatter) ? this : _innerFormatProvider.GetFormat(formatType) ?? this; } @@ -374,5 +376,29 @@ public void FormatProviderWithDestructuredProperties(string format, bool shouldU Assert.Contains(expectedFormattedDate, sw.ToString()); Assert.Contains(expectedFormattedNumber, sw.ToString()); } + + [Fact] + public void TraceAndSpanAreEmptyWhenAbsent() + { + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{TraceId}/{SpanId}", CultureInfo.InvariantCulture); + var evt = DelegatingSink.GetLogEvent(l => l.Information("Hello, world")); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal("/", sw.ToString()); + } + + [Fact] + public void TraceAndSpanAreIncludedWhenPresent() + { + var traceId = ActivityTraceId.CreateRandom(); + var spanId = ActivitySpanId.CreateRandom(); + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{TraceId}/{SpanId}", CultureInfo.InvariantCulture); + var evt = new LogEvent(DateTimeOffset.Now, LogEventLevel.Debug, null, + new MessageTemplate(Enumerable.Empty()), Enumerable.Empty(), + traceId, spanId); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal($"{traceId}/{spanId}", sw.ToString()); + } } } diff --git a/test/Serilog.Sinks.Console.Tests/Serilog.Sinks.Console.Tests.csproj b/test/Serilog.Sinks.Console.Tests/Serilog.Sinks.Console.Tests.csproj index 57b8025..472965f 100644 --- a/test/Serilog.Sinks.Console.Tests/Serilog.Sinks.Console.Tests.csproj +++ b/test/Serilog.Sinks.Console.Tests/Serilog.Sinks.Console.Tests.csproj @@ -1,12 +1,11 @@  - netcoreapp2.1;netcoreapp2.2;netcoreapp3.0;netcoreapp3.1;net452;net462;net472;net48;net5.0 + net7.0 true ../../assets/Serilog.snk true true - 8.0 enable From 30b4abb0fb6587c48d86317f38e9e9c4733ad5c0 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 12 Oct 2023 10:05:30 +1000 Subject: [PATCH 14/14] Update publishing key --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 397acc7..c6dd7f8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,7 +9,7 @@ artifacts: deploy: - provider: NuGet api_key: - secure: oemq1E4zMR+LKQyrR83ZLcugPpZtl5OMKjtpMy/mbPEwuFGS+Oe46427D9KoHYD8 + secure: dX4ewxGxhiNURkqJPuZQ8GQNjLvb8oZrHBThVotn+9GSMyQzeKXFpHkN04ykOfgc skip_symbols: true on: branch: /^(main|dev)$/