From 106c377a8f9f0ccedab309ede481ca10a12ee5c0 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Sat, 14 Jan 2023 08:34:54 +1000 Subject: [PATCH 1/3] 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 2/3] 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 3/3] 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);