From d2dd76d2f62a8c4eb35c333077bf25e00b625d40 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 31 May 2017 14:09:54 +1000 Subject: [PATCH 01/12] Initial work for #958 - stop allocating output properties during rendering --- src/Serilog/Events/ScalarValue.cs | 15 ++- .../Formatting/Display/LiteralStringValue.cs | 4 +- .../Formatting/Display/LogEventLevelValue.cs | 72 +++++++------- .../Display/LogEventPropertiesValue.cs | 12 ++- .../Display/LogEventPropertyMessageValue.cs | 1 + .../Display/MessageTemplateTextFormatter.cs | 98 ++++++++++++++----- .../Formatting/Display/OutputProperties.cs | 6 +- src/Serilog/Formatting/Display/Padding.cs | 21 ++-- src/Serilog/Parsing/PropertyToken.cs | 6 +- src/Serilog/Parsing/TextToken.cs | 3 +- src/Serilog/Serilog.csproj | 2 + 11 files changed, 157 insertions(+), 83 deletions(-) diff --git a/src/Serilog/Events/ScalarValue.cs b/src/Serilog/Events/ScalarValue.cs index e5b280d6f..01f514fe5 100644 --- a/src/Serilog/Events/ScalarValue.cs +++ b/src/Serilog/Events/ScalarValue.cs @@ -46,16 +46,21 @@ public ScalarValue(object value) /// A format provider to apply to the value, or null to use the default. /// . public override void Render(TextWriter output, string format = null, IFormatProvider formatProvider = null) + { + Render(Value, output, format, formatProvider); + } + + internal static void Render(object value, TextWriter output, string format = null, IFormatProvider formatProvider = null) { if (output == null) throw new ArgumentNullException(nameof(output)); - if (Value == null) + if (value == null) { output.Write("null"); return; } - var s = Value as string; + var s = value as string; if (s != null) { if (format != "l") @@ -76,19 +81,19 @@ public override void Render(TextWriter output, string format = null, IFormatProv var custom = (ICustomFormatter)formatProvider.GetFormat(typeof(ICustomFormatter)); if (custom != null) { - output.Write(custom.Format(format, Value, formatProvider)); + output.Write(custom.Format(format, value, formatProvider)); return; } } - var f = Value as IFormattable; + var f = value as IFormattable; if (f != null) { output.Write(f.ToString(format, formatProvider ?? CultureInfo.InvariantCulture)); } else { - output.Write(Value.ToString()); + output.Write(value.ToString()); } } diff --git a/src/Serilog/Formatting/Display/LiteralStringValue.cs b/src/Serilog/Formatting/Display/LiteralStringValue.cs index 82f172536..a056baca2 100644 --- a/src/Serilog/Formatting/Display/LiteralStringValue.cs +++ b/src/Serilog/Formatting/Display/LiteralStringValue.cs @@ -20,14 +20,14 @@ namespace Serilog.Formatting.Display { // A special case (non-null) string value for use in output // templates. Does not apply "quoted" formatting by default. + [Obsolete("Not used by the current output formatting implementation.")] class LiteralStringValue : LogEventPropertyValue { readonly string _value; public LiteralStringValue(string value) { - if (value == null) throw new ArgumentNullException(nameof(value)); - _value = value; + _value = value ?? throw new ArgumentNullException(nameof(value)); } public override void Render(TextWriter output, string format = null, IFormatProvider formatProvider = null) diff --git a/src/Serilog/Formatting/Display/LogEventLevelValue.cs b/src/Serilog/Formatting/Display/LogEventLevelValue.cs index d850722c3..055c25cf8 100644 --- a/src/Serilog/Formatting/Display/LogEventLevelValue.cs +++ b/src/Serilog/Formatting/Display/LogEventLevelValue.cs @@ -54,6 +54,7 @@ class LogEventLevelValue : LogEventPropertyValue new []{ "F", "FA", "FTL", "FATL" } }; + [Obsolete("Not used by the current output formatting implementation.")] public LogEventLevelValue(LogEventLevel value) { _value = value; @@ -64,48 +65,49 @@ public LogEventLevelValue(LogEventLevel value) /// public override void Render(TextWriter output, string format = null, IFormatProvider formatProvider = null) { - if (format != null && (format.Length == 2 || format.Length == 3)) + output.Write(GetLevelMoniker(_value, format)); + } + + public static string GetLevelMoniker(LogEventLevel value, string format = null) + { + if (format == null || format.Length != 2 && format.Length != 3) + return Casing.Format(value.ToString(), format); + + // 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) { - // 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'; - } + width *= 10; + width += format[2] - '0'; + } - if (width < 1) - return; + if (width < 1) + return string.Empty; - if (width > 4) - { - var value = _value.ToString(); - if (value.Length > width) - value = value.Substring(0, width); - output.Write(Casing.Format(value)); - return; - } + if (width > 4) + { + var stringValue = value.ToString(); + if (stringValue.Length > width) + stringValue = stringValue.Substring(0, width); + return Casing.Format(stringValue); + } - var index = (int)_value; - if (index >= 0 && index <= (int) LogEventLevel.Fatal) + var index = (int)value; + if (index >= 0 && index <= (int) LogEventLevel.Fatal) + { + switch (format[0]) { - switch (format[0]) - { - case 'w': - output.Write(_lowercaseLevelMap[index][width - 1]); - return; - case 'u': - output.Write(_uppercaseLevelMap[index][width - 1]); - return; - case 't': - output.Write(_titleCaseLevelMap[index][width - 1]); - return; - } + case 'w': + return _lowercaseLevelMap[index][width - 1]; + case 'u': + return _uppercaseLevelMap[index][width - 1]; + case 't': + return _titleCaseLevelMap[index][width - 1]; } } - output.Write(Casing.Format(_value.ToString(), format)); + return Casing.Format(value.ToString(), format); } } -} \ No newline at end of file +} diff --git a/src/Serilog/Formatting/Display/LogEventPropertiesValue.cs b/src/Serilog/Formatting/Display/LogEventPropertiesValue.cs index 1e47f0ee7..c58ac9384 100644 --- a/src/Serilog/Formatting/Display/LogEventPropertiesValue.cs +++ b/src/Serilog/Formatting/Display/LogEventPropertiesValue.cs @@ -25,6 +25,7 @@ class LogEventPropertiesValue : LogEventPropertyValue readonly IReadOnlyDictionary _properties; readonly MessageTemplate _outputTemplate; + [Obsolete("Not used by the current output formatting implementation.")] public LogEventPropertiesValue(MessageTemplate template, IReadOnlyDictionary properties, MessageTemplate outputTemplate) { _template = template; @@ -33,18 +34,23 @@ public LogEventPropertiesValue(MessageTemplate template, IReadOnlyDictionary properties, MessageTemplate outputTemplate, TextWriter output, IFormatProvider formatProvider = null) { output.Write('{'); var delim = ""; - foreach (var kvp in _properties) + foreach (var kvp in properties) { - if (TemplateContainsPropertyName(_template, kvp.Key)) + if (TemplateContainsPropertyName(template, kvp.Key)) { continue; } - if (TemplateContainsPropertyName(_outputTemplate, kvp.Key)) + if (TemplateContainsPropertyName(outputTemplate, kvp.Key)) { continue; } diff --git a/src/Serilog/Formatting/Display/LogEventPropertyMessageValue.cs b/src/Serilog/Formatting/Display/LogEventPropertyMessageValue.cs index f021962cd..d897ca914 100644 --- a/src/Serilog/Formatting/Display/LogEventPropertyMessageValue.cs +++ b/src/Serilog/Formatting/Display/LogEventPropertyMessageValue.cs @@ -19,6 +19,7 @@ namespace Serilog.Formatting.Display { + [Obsolete("Not used by the current output formatting implementation.")] class LogEventPropertyMessageValue : LogEventPropertyValue { readonly MessageTemplate _template; diff --git a/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs b/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs index 0c1ae7f8d..fe4ddb6e9 100644 --- a/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs +++ b/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs @@ -35,6 +35,8 @@ public class MessageTemplateTextFormatter : ITextFormatter readonly IFormatProvider _formatProvider; readonly MessageTemplate _outputTemplate; + static readonly IReadOnlyDictionary NoProperties = new Dictionary(); + /// /// Construct a . /// @@ -58,42 +60,90 @@ public void Format(LogEvent logEvent, TextWriter output) if (logEvent == null) throw new ArgumentNullException(nameof(logEvent)); if (output == null) throw new ArgumentNullException(nameof(output)); - // This could be lazier: the output properties include - // everything from the log event, but often we won't need any more than - // just the standard timestamp/message etc. - var outputProperties = OutputProperties.GetOutputProperties(logEvent, _outputTemplate); - foreach (var token in _outputTemplate.Tokens) { - var pt = token as PropertyToken; - if (pt == null) + if (token is TextToken tt) { - token.Render(outputProperties, output, _formatProvider); + tt.Render(NoProperties, output, _formatProvider); continue; } - // First variation from normal rendering - if a property is missing, - // don't render anything (message templates render the raw token here). - LogEventPropertyValue propertyValue; - if (!outputProperties.TryGetValue(pt.PropertyName, out propertyValue)) - continue; + var pt = (PropertyToken)token; - // Second variation; if the value is a scalar string, use literal - // rendering and support some additional formats: 'u' for uppercase - // and 'w' for lowercase. - var sv = propertyValue as ScalarValue; - if (sv != null && sv.Value is string) + if (pt.PropertyName == OutputProperties.MessagePropertyName) { - var overridden = new Dictionary + if (pt.Alignment.HasValue) { - { pt.PropertyName, new LiteralStringValue((string) sv.Value) } - }; - - token.Render(overridden, output, _formatProvider); + var sw = new StringWriter(); + logEvent.MessageTemplate.Render(logEvent.Properties, sw, _formatProvider); + Padding.Apply(output, sw.ToString(), pt.Alignment); + } + else + { + logEvent.MessageTemplate.Render(logEvent.Properties, output, _formatProvider); + } + } + else if (pt.PropertyName == OutputProperties.TimestampPropertyName) + { + if (pt.Alignment.HasValue) + { + var sw = new StringWriter(); + ScalarValue.Render(logEvent.Timestamp, sw, pt.Format, _formatProvider); + Padding.Apply(output, sw.ToString(), pt.Alignment); + } + else + { + ScalarValue.Render(logEvent.Timestamp, output, pt.Format, _formatProvider); + } + } + else if (pt.PropertyName == OutputProperties.LevelPropertyName) + { + var moniker = LogEventLevelValue.GetLevelMoniker(logEvent.Level, pt.Format); + Padding.Apply(output, moniker, pt.Alignment); + } + else if (pt.PropertyName == OutputProperties.NewLinePropertyName) + { + Padding.Apply(output, Environment.NewLine, pt.Alignment); + } + else if (pt.PropertyName == OutputProperties.ExceptionPropertyName) + { + var exception = logEvent.Exception == null ? "" : logEvent.Exception + Environment.NewLine; + Padding.Apply(output, exception, pt.Alignment); + } + else if (pt.PropertyName == OutputProperties.PropertiesPropertyName) + { + if (pt.Alignment.HasValue) + { + var sw = new StringWriter(); + LogEventPropertiesValue.Render(logEvent.MessageTemplate, logEvent.Properties, _outputTemplate, sw, _formatProvider); + Padding.Apply(output, sw.ToString(), pt.Alignment); + } + else + { + LogEventPropertiesValue.Render(logEvent.MessageTemplate, logEvent.Properties, _outputTemplate, output, _formatProvider); + } } else { - token.Render(outputProperties, output, _formatProvider); + // First variation from normal rendering - if a property is missing, + // don't render anything (message templates render the raw token here). + LogEventPropertyValue propertyValue; + if (!logEvent.Properties.TryGetValue(pt.PropertyName, out propertyValue)) + continue; + + // Second variation; if the value is a scalar string, use literal + // rendering and support some additional formats: 'u' for uppercase + // and 'w' for lowercase. + var sv = propertyValue as ScalarValue; + if (sv?.Value is string literalString) + { + var cased = Casing.Format(literalString, pt.Format); + Padding.Apply(output, cased, pt.Alignment); + } + else + { + token.Render(logEvent.Properties, output, _formatProvider); + } } } } diff --git a/src/Serilog/Formatting/Display/OutputProperties.cs b/src/Serilog/Formatting/Display/OutputProperties.cs index 14065c4db..022b64467 100644 --- a/src/Serilog/Formatting/Display/OutputProperties.cs +++ b/src/Serilog/Formatting/Display/OutputProperties.cs @@ -17,6 +17,8 @@ using System.Linq; using Serilog.Events; +#pragma warning disable 618 + namespace Serilog.Formatting.Display { /// @@ -62,7 +64,7 @@ public static class OutputProperties /// /// The log event. /// A dictionary with properties representing the log event. - [Obsolete("Pass the full output template using the other overload.")] + [Obsolete("These implementation details of output formatting will not be exposed in a future version.")] public static IReadOnlyDictionary GetOutputProperties(LogEvent logEvent) { return GetOutputProperties(logEvent, MessageTemplate.Empty); @@ -74,7 +76,7 @@ public static IReadOnlyDictionary GetOutputProper /// The log event. /// The output template. /// A dictionary with properties representing the log event. - public static IReadOnlyDictionary GetOutputProperties(LogEvent logEvent, MessageTemplate outputTemplate) + internal static IReadOnlyDictionary GetOutputProperties(LogEvent logEvent, MessageTemplate outputTemplate) { var result = logEvent.Properties.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); diff --git a/src/Serilog/Formatting/Display/Padding.cs b/src/Serilog/Formatting/Display/Padding.cs index de6744f34..6fcf7e0c2 100644 --- a/src/Serilog/Formatting/Display/Padding.cs +++ b/src/Serilog/Formatting/Display/Padding.cs @@ -6,12 +6,14 @@ namespace Serilog.Formatting.Display { static class Padding { + static readonly char[] PaddingChars = new string(' ', 80).ToCharArray(); + /// /// Writes the provided value to the output, applying direction-based padding when is provided. /// public static void Apply(TextWriter output, string value, Alignment? alignment) { - if (!alignment.HasValue) + if (!alignment.HasValue || value.Length <= alignment.Value.Width) { output.Write(value); return; @@ -19,13 +21,20 @@ public static void Apply(TextWriter output, string value, Alignment? alignment) var pad = alignment.Value.Width - value.Length; - if (alignment.Value.Direction == AlignmentDirection.Right) - output.Write(new string(' ', pad)); - - output.Write(value); - if (alignment.Value.Direction == AlignmentDirection.Left) + output.Write(value); + + if (pad <= PaddingChars.Length) + { + output.Write(PaddingChars, 0, pad); + } + else + { output.Write(new string(' ', pad)); + } + + if (alignment.Value.Direction == AlignmentDirection.Right) + output.Write(value); } } } \ No newline at end of file diff --git a/src/Serilog/Parsing/PropertyToken.cs b/src/Serilog/Parsing/PropertyToken.cs index ef4db7c3a..73d879926 100644 --- a/src/Serilog/Parsing/PropertyToken.cs +++ b/src/Serilog/Parsing/PropertyToken.cs @@ -57,12 +57,10 @@ public PropertyToken(string propertyName, string rawText, string formatObsolete, public PropertyToken(string propertyName, string rawText, string format = null, Alignment? alignment = null, Destructuring destructuring = Destructuring.Default, int startIndex = -1) : base(startIndex) { - if (propertyName == null) throw new ArgumentNullException(nameof(propertyName)); - if (rawText == null) throw new ArgumentNullException(nameof(rawText)); - PropertyName = propertyName; + PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName)); Format = format; Destructuring = destructuring; - _rawText = rawText; + _rawText = rawText ?? throw new ArgumentNullException(nameof(rawText)); Alignment = alignment; int position; diff --git a/src/Serilog/Parsing/TextToken.cs b/src/Serilog/Parsing/TextToken.cs index 9035d57d1..daf1531ca 100644 --- a/src/Serilog/Parsing/TextToken.cs +++ b/src/Serilog/Parsing/TextToken.cs @@ -32,8 +32,7 @@ public class TextToken : MessageTemplateToken /// public TextToken(string text, int startIndex = -1) : base(startIndex) { - if (text == null) throw new ArgumentNullException(nameof(text)); - Text = text; + Text = text ?? throw new ArgumentNullException(nameof(text)); } /// diff --git a/src/Serilog/Serilog.csproj b/src/Serilog/Serilog.csproj index 698c466f6..fed10897a 100644 --- a/src/Serilog/Serilog.csproj +++ b/src/Serilog/Serilog.csproj @@ -18,6 +18,8 @@ false true + True + From 391882a3dd3e1c86f0ea116b7ad71f3f9ed3b761 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 31 May 2017 14:19:57 +1000 Subject: [PATCH 02/12] Fix tests --- src/Serilog/Formatting/Display/Padding.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog/Formatting/Display/Padding.cs b/src/Serilog/Formatting/Display/Padding.cs index 6fcf7e0c2..7872d680f 100644 --- a/src/Serilog/Formatting/Display/Padding.cs +++ b/src/Serilog/Formatting/Display/Padding.cs @@ -13,7 +13,7 @@ static class Padding /// public static void Apply(TextWriter output, string value, Alignment? alignment) { - if (!alignment.HasValue || value.Length <= alignment.Value.Width) + if (!alignment.HasValue || value.Length >= alignment.Value.Width) { output.Write(value); return; From b02acd281df277d8d0d6282e6377344f309a4afc Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 31 May 2017 15:12:18 +1000 Subject: [PATCH 03/12] Separate current and obsolete formatting code --- src/Serilog/Formatting/Display/Casing.cs | 16 +++++++- ...ventLevelValue.cs => LevelOutputFormat.cs} | 35 ++++------------ .../Display/MessageTemplateTextFormatter.cs | 8 ++-- .../{ => Obsolete}/LiteralStringValue.cs | 2 +- .../Display/Obsolete/LogEventLevelValue.cs | 40 ++++++++++++++++++ .../Obsolete/LogEventPropertiesValue.cs | 41 +++++++++++++++++++ .../LogEventPropertyMessageValue.cs | 2 +- .../Formatting/Display/OutputProperties.cs | 5 ++- src/Serilog/Formatting/Display/Padding.cs | 16 +++++++- ...tiesValue.cs => PropertiesOutputFormat.cs} | 21 +--------- 10 files changed, 131 insertions(+), 55 deletions(-) rename src/Serilog/Formatting/Display/{LogEventLevelValue.cs => LevelOutputFormat.cs} (76%) rename src/Serilog/Formatting/Display/{ => Obsolete}/LiteralStringValue.cs (97%) create mode 100644 src/Serilog/Formatting/Display/Obsolete/LogEventLevelValue.cs create mode 100644 src/Serilog/Formatting/Display/Obsolete/LogEventPropertiesValue.cs rename src/Serilog/Formatting/Display/{ => Obsolete}/LogEventPropertyMessageValue.cs (96%) rename src/Serilog/Formatting/Display/{LogEventPropertiesValue.cs => PropertiesOutputFormat.cs} (70%) diff --git a/src/Serilog/Formatting/Display/Casing.cs b/src/Serilog/Formatting/Display/Casing.cs index b024e8b87..a4e717278 100644 --- a/src/Serilog/Formatting/Display/Casing.cs +++ b/src/Serilog/Formatting/Display/Casing.cs @@ -1,4 +1,18 @@ -namespace Serilog.Formatting.Display +// Copyright 2013-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. + +namespace Serilog.Formatting.Display { static class Casing { diff --git a/src/Serilog/Formatting/Display/LogEventLevelValue.cs b/src/Serilog/Formatting/Display/LevelOutputFormat.cs similarity index 76% rename from src/Serilog/Formatting/Display/LogEventLevelValue.cs rename to src/Serilog/Formatting/Display/LevelOutputFormat.cs index 055c25cf8..8e3977c70 100644 --- a/src/Serilog/Formatting/Display/LogEventLevelValue.cs +++ b/src/Serilog/Formatting/Display/LevelOutputFormat.cs @@ -1,4 +1,4 @@ -// Copyright 2013-2015 Serilog Contributors +// 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. @@ -12,21 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -using System; -using System.IO; - using Serilog.Events; namespace Serilog.Formatting.Display { - // Allows for the specific handling of 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). - class LogEventLevelValue : LogEventPropertyValue + /// + /// 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 { - readonly LogEventLevel _value; - static readonly string[][] _titleCaseLevelMap = { new []{ "V", "Vb", "Vrb", "Verb" }, new []{ "D", "De", "Dbg", "Dbug" }, @@ -54,20 +51,6 @@ class LogEventLevelValue : LogEventPropertyValue new []{ "F", "FA", "FTL", "FATL" } }; - [Obsolete("Not used by the current output formatting implementation.")] - public LogEventLevelValue(LogEventLevel value) - { - _value = value; - } - - /// - /// This method will apply only upper or lower case formatting, not fixed width - /// - public override void Render(TextWriter output, string format = null, IFormatProvider formatProvider = null) - { - output.Write(GetLevelMoniker(_value, format)); - } - public static string GetLevelMoniker(LogEventLevel value, string format = null) { if (format == null || format.Length != 2 && format.Length != 3) @@ -110,4 +93,4 @@ public static string GetLevelMoniker(LogEventLevel value, string format = null) return Casing.Format(value.ToString(), format); } } -} +} \ No newline at end of file diff --git a/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs b/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs index fe4ddb6e9..ee247f249 100644 --- a/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs +++ b/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs @@ -1,4 +1,4 @@ -// Copyright 2013-2015 Serilog Contributors +// Copyright 2013-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. @@ -98,7 +98,7 @@ public void Format(LogEvent logEvent, TextWriter output) } else if (pt.PropertyName == OutputProperties.LevelPropertyName) { - var moniker = LogEventLevelValue.GetLevelMoniker(logEvent.Level, pt.Format); + var moniker = LevelOutputFormat.GetLevelMoniker(logEvent.Level, pt.Format); Padding.Apply(output, moniker, pt.Alignment); } else if (pt.PropertyName == OutputProperties.NewLinePropertyName) @@ -115,12 +115,12 @@ public void Format(LogEvent logEvent, TextWriter output) if (pt.Alignment.HasValue) { var sw = new StringWriter(); - LogEventPropertiesValue.Render(logEvent.MessageTemplate, logEvent.Properties, _outputTemplate, sw, _formatProvider); + PropertiesOutputFormat.Render(logEvent.MessageTemplate, logEvent.Properties, _outputTemplate, sw, _formatProvider); Padding.Apply(output, sw.ToString(), pt.Alignment); } else { - LogEventPropertiesValue.Render(logEvent.MessageTemplate, logEvent.Properties, _outputTemplate, output, _formatProvider); + PropertiesOutputFormat.Render(logEvent.MessageTemplate, logEvent.Properties, _outputTemplate, output, _formatProvider); } } else diff --git a/src/Serilog/Formatting/Display/LiteralStringValue.cs b/src/Serilog/Formatting/Display/Obsolete/LiteralStringValue.cs similarity index 97% rename from src/Serilog/Formatting/Display/LiteralStringValue.cs rename to src/Serilog/Formatting/Display/Obsolete/LiteralStringValue.cs index a056baca2..f537c5e24 100644 --- a/src/Serilog/Formatting/Display/LiteralStringValue.cs +++ b/src/Serilog/Formatting/Display/Obsolete/LiteralStringValue.cs @@ -16,7 +16,7 @@ using System.IO; using Serilog.Events; -namespace Serilog.Formatting.Display +namespace Serilog.Formatting.Display.Obsolete { // A special case (non-null) string value for use in output // templates. Does not apply "quoted" formatting by default. diff --git a/src/Serilog/Formatting/Display/Obsolete/LogEventLevelValue.cs b/src/Serilog/Formatting/Display/Obsolete/LogEventLevelValue.cs new file mode 100644 index 000000000..293990f5a --- /dev/null +++ b/src/Serilog/Formatting/Display/Obsolete/LogEventLevelValue.cs @@ -0,0 +1,40 @@ +// Copyright 2013-2015 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 System; +using System.IO; + +using Serilog.Events; + +namespace Serilog.Formatting.Display.Obsolete +{ + [Obsolete("Not used by the current output formatting implementation.")] + class LogEventLevelValue : LogEventPropertyValue + { + readonly LogEventLevel _value; + + public LogEventLevelValue(LogEventLevel value) + { + _value = value; + } + + /// + /// This method will apply only upper or lower case formatting, not fixed width + /// + public override void Render(TextWriter output, string format = null, IFormatProvider formatProvider = null) + { + output.Write(LevelOutputFormat.GetLevelMoniker(_value, format)); + } + } +} diff --git a/src/Serilog/Formatting/Display/Obsolete/LogEventPropertiesValue.cs b/src/Serilog/Formatting/Display/Obsolete/LogEventPropertiesValue.cs new file mode 100644 index 000000000..a0b089152 --- /dev/null +++ b/src/Serilog/Formatting/Display/Obsolete/LogEventPropertiesValue.cs @@ -0,0 +1,41 @@ +// 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 System; +using System.Collections.Generic; +using System.IO; +using Serilog.Events; + +namespace Serilog.Formatting.Display.Obsolete +{ + [Obsolete("Not used by the current output formatting implementation.")] + class LogEventPropertiesValue : LogEventPropertyValue + { + readonly MessageTemplate _template; + readonly IReadOnlyDictionary _properties; + readonly MessageTemplate _outputTemplate; + + public LogEventPropertiesValue(MessageTemplate template, IReadOnlyDictionary properties, MessageTemplate outputTemplate) + { + _template = template; + _properties = properties; + _outputTemplate = outputTemplate; + } + + public override void Render(TextWriter output, string format = null, IFormatProvider formatProvider = null) + { + PropertiesOutputFormat.Render(_template, _properties, _outputTemplate, output, formatProvider); + } + } +} \ No newline at end of file diff --git a/src/Serilog/Formatting/Display/LogEventPropertyMessageValue.cs b/src/Serilog/Formatting/Display/Obsolete/LogEventPropertyMessageValue.cs similarity index 96% rename from src/Serilog/Formatting/Display/LogEventPropertyMessageValue.cs rename to src/Serilog/Formatting/Display/Obsolete/LogEventPropertyMessageValue.cs index d897ca914..9507a7fb2 100644 --- a/src/Serilog/Formatting/Display/LogEventPropertyMessageValue.cs +++ b/src/Serilog/Formatting/Display/Obsolete/LogEventPropertyMessageValue.cs @@ -17,7 +17,7 @@ using System.IO; using Serilog.Events; -namespace Serilog.Formatting.Display +namespace Serilog.Formatting.Display.Obsolete { [Obsolete("Not used by the current output formatting implementation.")] class LogEventPropertyMessageValue : LogEventPropertyValue diff --git a/src/Serilog/Formatting/Display/OutputProperties.cs b/src/Serilog/Formatting/Display/OutputProperties.cs index 022b64467..c1b2cf8c8 100644 --- a/src/Serilog/Formatting/Display/OutputProperties.cs +++ b/src/Serilog/Formatting/Display/OutputProperties.cs @@ -1,4 +1,4 @@ -// Copyright 2013-2015 Serilog Contributors +// Copyright 2013-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. @@ -16,6 +16,7 @@ using System.Collections.Generic; using System.Linq; using Serilog.Events; +using Serilog.Formatting.Display.Obsolete; #pragma warning disable 618 @@ -96,4 +97,4 @@ internal static IReadOnlyDictionary GetOutputProp return result; } } -} \ No newline at end of file +} diff --git a/src/Serilog/Formatting/Display/Padding.cs b/src/Serilog/Formatting/Display/Padding.cs index 7872d680f..bd0e059fc 100644 --- a/src/Serilog/Formatting/Display/Padding.cs +++ b/src/Serilog/Formatting/Display/Padding.cs @@ -1,4 +1,18 @@ -using System.IO; +// Copyright 2013-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 System.IO; using Serilog.Parsing; diff --git a/src/Serilog/Formatting/Display/LogEventPropertiesValue.cs b/src/Serilog/Formatting/Display/PropertiesOutputFormat.cs similarity index 70% rename from src/Serilog/Formatting/Display/LogEventPropertiesValue.cs rename to src/Serilog/Formatting/Display/PropertiesOutputFormat.cs index c58ac9384..ac2b5b807 100644 --- a/src/Serilog/Formatting/Display/LogEventPropertiesValue.cs +++ b/src/Serilog/Formatting/Display/PropertiesOutputFormat.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Serilog Contributors +// 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. @@ -19,25 +19,8 @@ namespace Serilog.Formatting.Display { - class LogEventPropertiesValue : LogEventPropertyValue + static class PropertiesOutputFormat { - readonly MessageTemplate _template; - readonly IReadOnlyDictionary _properties; - readonly MessageTemplate _outputTemplate; - - [Obsolete("Not used by the current output formatting implementation.")] - public LogEventPropertiesValue(MessageTemplate template, IReadOnlyDictionary properties, MessageTemplate outputTemplate) - { - _template = template; - _properties = properties; - _outputTemplate = outputTemplate; - } - - public override void Render(TextWriter output, string format = null, IFormatProvider formatProvider = null) - { - Render(_template, _properties, _outputTemplate, output, formatProvider); - } - public static void Render(MessageTemplate template, IReadOnlyDictionary properties, MessageTemplate outputTemplate, TextWriter output, IFormatProvider formatProvider = null) { output.Write('{'); From ffee0dba4d4182f913cc71e2b4284a7f72b33832 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 31 May 2017 22:22:45 +1000 Subject: [PATCH 04/12] Separate rendering of message templates from their representation --- .../Data/LogEventPropertyValueVisitor.cs | 4 +- src/Serilog/Events/MessageTemplate.cs | 15 ++- .../Display/MessageTemplateRenderer.cs | 100 ++++++++++++++++++ .../Display/MessageTemplateTextFormatter.cs | 4 +- src/Serilog/Formatting/Json/JsonFormatter.cs | 3 +- src/Serilog/Parsing/PropertyToken.cs | 27 +---- 6 files changed, 116 insertions(+), 37 deletions(-) create mode 100644 src/Serilog/Formatting/Display/MessageTemplateRenderer.cs diff --git a/src/Serilog/Data/LogEventPropertyValueVisitor.cs b/src/Serilog/Data/LogEventPropertyValueVisitor.cs index dd1ae1687..d4bea708c 100644 --- a/src/Serilog/Data/LogEventPropertyValueVisitor.cs +++ b/src/Serilog/Data/LogEventPropertyValueVisitor.cs @@ -15,6 +15,8 @@ using System; using Serilog.Events; +// ReSharper disable VirtualMemberNeverOverridden.Global + namespace Serilog.Data { /// @@ -40,7 +42,6 @@ public abstract class LogEventPropertyValueVisitor /// Operation state. /// The value to visit. /// The result of visiting . - // ReSharper disable once VirtualMemberNeverOverriden.Global protected virtual TResult Visit(TState state, LogEventPropertyValue value) { if (value == null) throw new ArgumentNullException(nameof(value)); @@ -103,7 +104,6 @@ protected virtual TResult Visit(TState state, LogEventPropertyValue value) /// The value to visit. /// The result of visiting . // ReSharper disable once UnusedParameter.Global - // ReSharper disable once VirtualMemberNeverOverriden.Global protected virtual TResult VisitUnsupportedValue(TState state, LogEventPropertyValue value) { if (value == null) throw new ArgumentNullException(nameof(value)); diff --git a/src/Serilog/Events/MessageTemplate.cs b/src/Serilog/Events/MessageTemplate.cs index bb75f7234..55daeb35d 100644 --- a/src/Serilog/Events/MessageTemplate.cs +++ b/src/Serilog/Events/MessageTemplate.cs @@ -17,6 +17,7 @@ using System.IO; using System.Linq; using Serilog.Debugging; +using Serilog.Formatting.Display; using Serilog.Parsing; namespace Serilog.Events @@ -35,15 +36,14 @@ public class MessageTemplate readonly MessageTemplateToken[] _tokens; - // Optimisation for when the template is bound to - // property values. - /// /// Construct a message template using manually-defined text and property tokens. /// /// The text and property tokens defining the template. public MessageTemplate(IEnumerable tokens) + // ReSharper disable PossibleMultipleEnumeration : this(string.Join("", tokens), tokens) + // ReSharper enable PossibleMultipleEnumeration { } @@ -91,7 +91,7 @@ public MessageTemplate(string text, IEnumerable tokens) /// /// Similar to , but faster. /// - static TResult[] GetElementsOfTypeToArray(object[] tokens) + static TResult[] GetElementsOfTypeToArray(MessageTemplateToken[] tokens) where TResult: class { var result = new List(tokens.Length / 2); @@ -125,6 +125,8 @@ public override string ToString() /// public IEnumerable Tokens => _tokens; + internal MessageTemplateToken[] TokenArray => _tokens; + internal PropertyToken[] NamedProperties { get; } internal PropertyToken[] PositionalProperties { get; } @@ -156,10 +158,7 @@ public string Render(IReadOnlyDictionary properti /// Supplies culture-specific formatting information, or null. public void Render(IReadOnlyDictionary properties, TextWriter output, IFormatProvider formatProvider = null) { - foreach (var token in _tokens) - { - token.Render(properties, output, formatProvider); - } + MessageTemplateRenderer.Render(this, properties, output, null, formatProvider); } } } diff --git a/src/Serilog/Formatting/Display/MessageTemplateRenderer.cs b/src/Serilog/Formatting/Display/MessageTemplateRenderer.cs new file mode 100644 index 000000000..34fa4b305 --- /dev/null +++ b/src/Serilog/Formatting/Display/MessageTemplateRenderer.cs @@ -0,0 +1,100 @@ +// Copyright 2013-2015 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 System; +using System.Collections.Generic; +using System.IO; +using Serilog.Events; +using Serilog.Formatting.Json; +using Serilog.Parsing; + +namespace Serilog.Formatting.Display +{ + static class MessageTemplateRenderer + { + static JsonValueFormatter JsonValueFormatter = new JsonValueFormatter("$type"); + + public static void Render(MessageTemplate messageTemplate, IReadOnlyDictionary properties, TextWriter output, string format = null, IFormatProvider formatProvider = null) + { + if (messageTemplate == null) throw new ArgumentNullException(nameof(messageTemplate)); + if (properties == null) throw new ArgumentNullException(nameof(properties)); + if (output == null) throw new ArgumentNullException(nameof(output)); + + bool isLiteral = false, isJson = false; + + if (format != null && (format.Length == 1 || format.Length == 2)) + { + isLiteral = format[0] == 'l' || format[1] == 'l'; + isJson = format[0] == 'j' || format[1] == 'j'; + } + + foreach (var token in messageTemplate.TokenArray) + { + if (token is TextToken tt) + { + output.Write(tt.Text); + } + else + { + var pt = (PropertyToken) token; + RenderPropertyToken(pt, properties, output, formatProvider, isLiteral, isJson); + } + } + } + + public static void RenderPropertyToken(PropertyToken pt, IReadOnlyDictionary properties, TextWriter output, IFormatProvider formatProvider, bool isLiteral, bool isJson) + { + LogEventPropertyValue propertyValue; + if (!properties.TryGetValue(pt.PropertyName, out propertyValue)) + { + output.Write(pt.RawText); + return; + } + + if (!pt.Alignment.HasValue) + { + RenderValue(propertyValue, isLiteral, isJson, output, pt.Format, formatProvider); + return; + } + + var valueOutput = new StringWriter(); + RenderValue(propertyValue, isLiteral, isJson, valueOutput, pt.Format, formatProvider); + var value = valueOutput.ToString(); + + if (value.Length >= pt.Alignment.Value.Width) + { + output.Write(value); + return; + } + + Padding.Apply(output, value, pt.Alignment.Value); + } + + static void RenderValue(LogEventPropertyValue propertyValue, bool literal, bool json, TextWriter output, string format, IFormatProvider formatProvider) + { + if (literal && propertyValue is ScalarValue sv && sv.Value is string str) + { + output.Write(str); + } + else if (json) + { + JsonValueFormatter.Format(propertyValue, output); + } + else + { + propertyValue.Render(output, format, formatProvider); + } + } + } +} diff --git a/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs b/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs index ee247f249..1013b8f14 100644 --- a/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs +++ b/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs @@ -75,12 +75,12 @@ public void Format(LogEvent logEvent, TextWriter output) if (pt.Alignment.HasValue) { var sw = new StringWriter(); - logEvent.MessageTemplate.Render(logEvent.Properties, sw, _formatProvider); + MessageTemplateRenderer.Render(logEvent.MessageTemplate, logEvent.Properties, sw, pt.Format, _formatProvider); Padding.Apply(output, sw.ToString(), pt.Alignment); } else { - logEvent.MessageTemplate.Render(logEvent.Properties, output, _formatProvider); + MessageTemplateRenderer.Render(logEvent.MessageTemplate, logEvent.Properties, output, pt.Format, _formatProvider); } } else if (pt.PropertyName == OutputProperties.TimestampPropertyName) diff --git a/src/Serilog/Formatting/Json/JsonFormatter.cs b/src/Serilog/Formatting/Json/JsonFormatter.cs index aded1eb29..ee97bcc61 100644 --- a/src/Serilog/Formatting/Json/JsonFormatter.cs +++ b/src/Serilog/Formatting/Json/JsonFormatter.cs @@ -20,6 +20,7 @@ using System.IO; using System.Linq; using Serilog.Events; +using Serilog.Formatting.Display; using Serilog.Parsing; namespace Serilog.Formatting.Json @@ -207,7 +208,7 @@ protected virtual void WriteRenderingsValues(IGrouping[] WriteJsonProperty("Format", format.Format, ref eldelim, output); var sw = new StringWriter(); - format.Render(properties, sw); + MessageTemplateRenderer.RenderPropertyToken(format, properties, sw, _formatProvider, true, false); WriteJsonProperty("Rendering", sw.ToString(), ref eldelim, output); output.Write("}"); diff --git a/src/Serilog/Parsing/PropertyToken.cs b/src/Serilog/Parsing/PropertyToken.cs index 73d879926..403ce5e3d 100644 --- a/src/Serilog/Parsing/PropertyToken.cs +++ b/src/Serilog/Parsing/PropertyToken.cs @@ -87,30 +87,7 @@ public override void Render(IReadOnlyDictionary p if (properties == null) throw new ArgumentNullException(nameof(properties)); if (output == null) throw new ArgumentNullException(nameof(output)); - LogEventPropertyValue propertyValue; - if (!properties.TryGetValue(PropertyName, out propertyValue)) - { - output.Write(_rawText); - return; - } - - if (!Alignment.HasValue) - { - propertyValue.Render(output, Format, formatProvider); - return; - } - - var valueOutput = new StringWriter(); - propertyValue.Render(valueOutput, Format, formatProvider); - var value = valueOutput.ToString(); - - if (value.Length >= Alignment.Value.Width) - { - output.Write(value); - return; - } - - Padding.Apply(output, value, Alignment.Value); + MessageTemplateRenderer.RenderPropertyToken(this, properties, output, formatProvider, false, false); } /// @@ -138,6 +115,8 @@ public override void Render(IReadOnlyDictionary p /// public bool IsPositional => _position.HasValue; + internal string RawText => _rawText; + /// /// Try to get the integer value represented by the property name. /// From a74938cda85d00257e28e661649a277a0e21aacc Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 1 Jun 2017 20:14:03 +1000 Subject: [PATCH 05/12] Namespace layout and RESHARPER GREEN! --- Serilog.sln.DotSettings | 13 ++- .../{Parameters => Capturing}/DepthLimiter.cs | 2 +- .../GetablePropertyFinder.cs | 2 +- .../MessageTemplateProcessor.cs | 4 +- .../PropertyBinder.cs | 2 +- .../PropertyValueConverter.cs | 4 +- src/Serilog/Context/LogContext.cs | 1 - src/Serilog/Core/Logger.cs | 2 +- .../Core/Pipeline/MessageTemplateCache.cs | 6 +- src/Serilog/Events/MessageTemplate.cs | 2 +- .../Display/MessageTemplateTextFormatter.cs | 1 + src/Serilog/Formatting/Json/JsonFormatter.cs | 2 +- src/Serilog/LoggerConfiguration.cs | 2 +- src/Serilog/Parsing/PropertyToken.cs | 2 +- .../MessageTemplateRenderer.cs | 3 +- .../LevelControlBenchmark.cs | 4 - .../LogContextEnrichmentBenchmark.cs | 4 - .../MessageTemplateParsingBenchmark.cs | 8 -- .../NestedLoggerLatencyBenchmark.cs | 4 - .../PipelineBenchmark.cs | 8 -- test/Serilog.PerformanceTests/Support/Some.cs | 3 - .../Core/LogEventPropertyCapturingTests.cs | 2 +- .../Core/MessageTemplateTests.cs | 2 +- .../Core/SecondaryLoggerSinkTests.cs | 13 +-- .../Events/LogEventPropertyValueTests.cs | 2 +- .../Json/JsonValueFormatterTests.cs | 3 - .../Serilog.Tests/LoggerConfigurationTests.cs | 13 ++- .../MethodOverloadConventionTests.cs | 83 ++++++++++--------- .../Parameters/PropertyValueConverterTests.cs | 2 +- .../Settings/KeyValuePairSettingsTests.cs | 5 +- .../Settings/SettingValueConversionsTests.cs | 2 +- .../Serilog.Tests/Support/DisposableLogger.cs | 2 +- ...EventPropertyStructuralEqualityComparer.cs | 3 +- .../Support/LogEventPropertyValueComparer.cs | 2 +- test/Serilog.Tests/Support/StringSink.cs | 3 +- test/TestDummies/DummyThreadIdEnricher.cs | 6 +- test/TestDummies/Properties/AssemblyInfo.cs | 1 - 37 files changed, 97 insertions(+), 126 deletions(-) rename src/Serilog/{Parameters => Capturing}/DepthLimiter.cs (98%) rename src/Serilog/{Parameters => Capturing}/GetablePropertyFinder.cs (98%) rename src/Serilog/{Parameters => Capturing}/MessageTemplateProcessor.cs (92%) rename src/Serilog/{Parameters => Capturing}/PropertyBinder.cs (99%) rename src/Serilog/{Parameters => Capturing}/PropertyValueConverter.cs (99%) mode change 100755 => 100644 rename src/Serilog/{Formatting/Display => Rendering}/MessageTemplateRenderer.cs (98%) diff --git a/Serilog.sln.DotSettings b/Serilog.sln.DotSettings index 77a52ea2d..a84b8ae38 100644 --- a/Serilog.sln.DotSettings +++ b/Serilog.sln.DotSettings @@ -2,10 +2,13 @@ True + True True True False SOLUTION + DO_NOT_SHOW + DO_NOT_SHOW DO_NOT_SHOW ERROR DO_NOT_SHOW @@ -16,6 +19,7 @@ ERROR WARNING ERROR + HINT ERROR ERROR ERROR @@ -27,8 +31,9 @@ ERROR DO_NOT_SHOW DO_NOT_SHOW - HINT + DO_NOT_SHOW DO_NOT_SHOW + HINT DO_NOT_SHOW HINT ERROR @@ -103,6 +108,7 @@ DO_NOT_SHOW SUGGESTION ERROR + HINT ERROR ERROR ERROR @@ -111,6 +117,8 @@ <?xml version="1.0" encoding="utf-16"?><Profile name="Format My Code Using &quot;Particular&quot; conventions"><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSUseVar><BehavourStyle>CAN_CHANGE_TO_IMPLICIT</BehavourStyle><LocalVariableStyle>ALWAYS_IMPLICIT</LocalVariableStyle><ForeachVariableStyle>ALWAYS_IMPLICIT</ForeachVariableStyle></CSUseVar><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSReformatCode>True</CSReformatCode><CSReorderTypeMembers>True</CSReorderTypeMembers><JsInsertSemicolon>True</JsInsertSemicolon><JsReformatCode>True</JsReformatCode><CssReformatCode>True</CssReformatCode><CSArrangeThisQualifier>True</CSArrangeThisQualifier><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><HtmlReformatCode>True</HtmlReformatCode><CSShortenReferences>True</CSShortenReferences><CSharpFormatDocComments>True</CSharpFormatDocComments><CssAlphabetizeProperties>True</CssAlphabetizeProperties></Profile> Default: Reformat Code Format My Code Using "Particular" conventions + Implicit + Implicit False DO_NOT_CHANGE DO_NOT_CHANGE @@ -477,10 +485,13 @@ II.2.12 <HandlesEvent /> True Automatic property True + False False False + AD DB DTC + GT ID NSB SLA diff --git a/src/Serilog/Parameters/DepthLimiter.cs b/src/Serilog/Capturing/DepthLimiter.cs similarity index 98% rename from src/Serilog/Parameters/DepthLimiter.cs rename to src/Serilog/Capturing/DepthLimiter.cs index e51b23794..1b5150536 100644 --- a/src/Serilog/Parameters/DepthLimiter.cs +++ b/src/Serilog/Capturing/DepthLimiter.cs @@ -17,7 +17,7 @@ using Serilog.Events; using Serilog.Parsing; -namespace Serilog.Parameters +namespace Serilog.Capturing { partial class PropertyValueConverter { diff --git a/src/Serilog/Parameters/GetablePropertyFinder.cs b/src/Serilog/Capturing/GetablePropertyFinder.cs similarity index 98% rename from src/Serilog/Parameters/GetablePropertyFinder.cs rename to src/Serilog/Capturing/GetablePropertyFinder.cs index e7304547f..9c0f74e06 100644 --- a/src/Serilog/Parameters/GetablePropertyFinder.cs +++ b/src/Serilog/Capturing/GetablePropertyFinder.cs @@ -17,7 +17,7 @@ using System.Linq; using System.Reflection; -namespace Serilog.Parameters +namespace Serilog.Capturing { static class GetablePropertyFinder { diff --git a/src/Serilog/Parameters/MessageTemplateProcessor.cs b/src/Serilog/Capturing/MessageTemplateProcessor.cs similarity index 92% rename from src/Serilog/Parameters/MessageTemplateProcessor.cs rename to src/Serilog/Capturing/MessageTemplateProcessor.cs index 780fed64d..974f6fe8a 100644 --- a/src/Serilog/Parameters/MessageTemplateProcessor.cs +++ b/src/Serilog/Capturing/MessageTemplateProcessor.cs @@ -18,11 +18,11 @@ using Serilog.Events; using Serilog.Parsing; -namespace Serilog.Parameters +namespace Serilog.Capturing { class MessageTemplateProcessor : ILogEventPropertyFactory { - readonly IMessageTemplateParser _parser = new MessageTemplateCache(new MessageTemplateParser()); + readonly MessageTemplateCache _parser = new MessageTemplateCache(new MessageTemplateParser()); readonly PropertyBinder _propertyBinder; readonly PropertyValueConverter _propertyValueConverter; diff --git a/src/Serilog/Parameters/PropertyBinder.cs b/src/Serilog/Capturing/PropertyBinder.cs similarity index 99% rename from src/Serilog/Parameters/PropertyBinder.cs rename to src/Serilog/Capturing/PropertyBinder.cs index 1461f61c5..23ebd6047 100644 --- a/src/Serilog/Parameters/PropertyBinder.cs +++ b/src/Serilog/Capturing/PropertyBinder.cs @@ -19,7 +19,7 @@ using Serilog.Events; using Serilog.Parsing; -namespace Serilog.Parameters +namespace Serilog.Capturing { // Performance relevant - on the hot path when creating log events from existing templates. class PropertyBinder diff --git a/src/Serilog/Parameters/PropertyValueConverter.cs b/src/Serilog/Capturing/PropertyValueConverter.cs old mode 100755 new mode 100644 similarity index 99% rename from src/Serilog/Parameters/PropertyValueConverter.cs rename to src/Serilog/Capturing/PropertyValueConverter.cs index 712af6f18..6b3cc53b8 --- a/src/Serilog/Parameters/PropertyValueConverter.cs +++ b/src/Serilog/Capturing/PropertyValueConverter.cs @@ -24,7 +24,7 @@ using Serilog.Policies; using System.Runtime.CompilerServices; -namespace Serilog.Parameters +namespace Serilog.Capturing { // Values in Serilog are simplified down into a lowest-common-denominator internal // type system so that there is a better chance of code written with one sink in @@ -258,7 +258,7 @@ IEnumerable GetProperties(object value, ILogEventPropertyValue if (_propagateExceptions) throw; - propValue = "The property accessor threw an exception: " + ex.InnerException.GetType().Name; + propValue = "The property accessor threw an exception: " + ex.InnerException?.GetType().Name; } yield return new LogEventProperty(prop.Name, recursive.CreatePropertyValue(propValue, true)); } diff --git a/src/Serilog/Context/LogContext.cs b/src/Serilog/Context/LogContext.cs index b4ebd2e37..8d2657134 100644 --- a/src/Serilog/Context/LogContext.cs +++ b/src/Serilog/Context/LogContext.cs @@ -20,7 +20,6 @@ using Serilog.Events; #if ASYNCLOCAL -using System.Collections.Generic; using System.Threading; #elif REMOTING using System.Runtime.Remoting; diff --git a/src/Serilog/Core/Logger.cs b/src/Serilog/Core/Logger.cs index 141b6363d..b9207c31d 100644 --- a/src/Serilog/Core/Logger.cs +++ b/src/Serilog/Core/Logger.cs @@ -14,10 +14,10 @@ using System; using System.Collections.Generic; +using Serilog.Capturing; using Serilog.Core.Enrichers; using Serilog.Debugging; using Serilog.Events; -using Serilog.Parameters; #pragma warning disable Serilog004 // Constant MessageTemplate verifier diff --git a/src/Serilog/Core/Pipeline/MessageTemplateCache.cs b/src/Serilog/Core/Pipeline/MessageTemplateCache.cs index 0688ee0e1..459a224a5 100644 --- a/src/Serilog/Core/Pipeline/MessageTemplateCache.cs +++ b/src/Serilog/Core/Pipeline/MessageTemplateCache.cs @@ -13,9 +13,13 @@ // limitations under the License. using System; -using System.Collections.Generic; using Serilog.Events; + +#if HASHTABLE using System.Collections; +#else +using System.Collections.Generic; +#endif namespace Serilog.Core.Pipeline { diff --git a/src/Serilog/Events/MessageTemplate.cs b/src/Serilog/Events/MessageTemplate.cs index 55daeb35d..240cee9ed 100644 --- a/src/Serilog/Events/MessageTemplate.cs +++ b/src/Serilog/Events/MessageTemplate.cs @@ -17,8 +17,8 @@ using System.IO; using System.Linq; using Serilog.Debugging; -using Serilog.Formatting.Display; using Serilog.Parsing; +using Serilog.Rendering; namespace Serilog.Events { diff --git a/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs b/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs index 1013b8f14..5295d7af1 100644 --- a/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs +++ b/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs @@ -17,6 +17,7 @@ using System.IO; using Serilog.Events; using Serilog.Parsing; +using Serilog.Rendering; namespace Serilog.Formatting.Display { diff --git a/src/Serilog/Formatting/Json/JsonFormatter.cs b/src/Serilog/Formatting/Json/JsonFormatter.cs index ee97bcc61..5e762ad7c 100644 --- a/src/Serilog/Formatting/Json/JsonFormatter.cs +++ b/src/Serilog/Formatting/Json/JsonFormatter.cs @@ -20,8 +20,8 @@ using System.IO; using System.Linq; using Serilog.Events; -using Serilog.Formatting.Display; using Serilog.Parsing; +using Serilog.Rendering; namespace Serilog.Formatting.Json { diff --git a/src/Serilog/LoggerConfiguration.cs b/src/Serilog/LoggerConfiguration.cs index bc39b5e4b..f24e2dc6c 100644 --- a/src/Serilog/LoggerConfiguration.cs +++ b/src/Serilog/LoggerConfiguration.cs @@ -15,12 +15,12 @@ using System; using System.Collections.Generic; using System.Linq; +using Serilog.Capturing; using Serilog.Configuration; using Serilog.Core; using Serilog.Core.Enrichers; using Serilog.Core.Sinks; using Serilog.Events; -using Serilog.Parameters; namespace Serilog { diff --git a/src/Serilog/Parsing/PropertyToken.cs b/src/Serilog/Parsing/PropertyToken.cs index 403ce5e3d..282f9b1e2 100644 --- a/src/Serilog/Parsing/PropertyToken.cs +++ b/src/Serilog/Parsing/PropertyToken.cs @@ -18,7 +18,7 @@ using System.Globalization; using System.IO; using Serilog.Events; -using Serilog.Formatting.Display; +using Serilog.Rendering; namespace Serilog.Parsing { diff --git a/src/Serilog/Formatting/Display/MessageTemplateRenderer.cs b/src/Serilog/Rendering/MessageTemplateRenderer.cs similarity index 98% rename from src/Serilog/Formatting/Display/MessageTemplateRenderer.cs rename to src/Serilog/Rendering/MessageTemplateRenderer.cs index 34fa4b305..446e52556 100644 --- a/src/Serilog/Formatting/Display/MessageTemplateRenderer.cs +++ b/src/Serilog/Rendering/MessageTemplateRenderer.cs @@ -16,10 +16,11 @@ using System.Collections.Generic; using System.IO; using Serilog.Events; +using Serilog.Formatting.Display; using Serilog.Formatting.Json; using Serilog.Parsing; -namespace Serilog.Formatting.Display +namespace Serilog.Rendering { static class MessageTemplateRenderer { diff --git a/test/Serilog.PerformanceTests/LevelControlBenchmark.cs b/test/Serilog.PerformanceTests/LevelControlBenchmark.cs index 899212357..6f29e9883 100644 --- a/test/Serilog.PerformanceTests/LevelControlBenchmark.cs +++ b/test/Serilog.PerformanceTests/LevelControlBenchmark.cs @@ -1,11 +1,7 @@ using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; -using Serilog; using Serilog.Core; using Serilog.Events; -using System; using Serilog.PerformanceTests.Support; -using Xunit; namespace Serilog.PerformanceTests { diff --git a/test/Serilog.PerformanceTests/LogContextEnrichmentBenchmark.cs b/test/Serilog.PerformanceTests/LogContextEnrichmentBenchmark.cs index c353fccfb..e7ea19867 100644 --- a/test/Serilog.PerformanceTests/LogContextEnrichmentBenchmark.cs +++ b/test/Serilog.PerformanceTests/LogContextEnrichmentBenchmark.cs @@ -1,10 +1,6 @@ using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; -using Serilog; using Serilog.Context; -using System; using Serilog.PerformanceTests.Support; -using Xunit; using Serilog.Events; namespace Serilog.PerformanceTests diff --git a/test/Serilog.PerformanceTests/MessageTemplateParsingBenchmark.cs b/test/Serilog.PerformanceTests/MessageTemplateParsingBenchmark.cs index d9c3fcfe3..a8320447a 100644 --- a/test/Serilog.PerformanceTests/MessageTemplateParsingBenchmark.cs +++ b/test/Serilog.PerformanceTests/MessageTemplateParsingBenchmark.cs @@ -1,13 +1,5 @@ using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; -using Serilog; -using Serilog.Events; using Serilog.Parsing; -using System; -using System.Linq; -using System.Collections; -using System.Collections.Generic; -using Xunit; namespace Serilog.PerformanceTests { diff --git a/test/Serilog.PerformanceTests/NestedLoggerLatencyBenchmark.cs b/test/Serilog.PerformanceTests/NestedLoggerLatencyBenchmark.cs index 537a60371..578fd6de3 100644 --- a/test/Serilog.PerformanceTests/NestedLoggerLatencyBenchmark.cs +++ b/test/Serilog.PerformanceTests/NestedLoggerLatencyBenchmark.cs @@ -1,9 +1,5 @@ using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; -using Serilog; -using System; using Serilog.PerformanceTests.Support; -using Xunit; using Serilog.Events; namespace Serilog.PerformanceTests diff --git a/test/Serilog.PerformanceTests/PipelineBenchmark.cs b/test/Serilog.PerformanceTests/PipelineBenchmark.cs index 6812f2345..dc5440604 100644 --- a/test/Serilog.PerformanceTests/PipelineBenchmark.cs +++ b/test/Serilog.PerformanceTests/PipelineBenchmark.cs @@ -14,16 +14,8 @@ // limitations under the License. using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; -using Serilog; -using Serilog.Events; -using Serilog.Parsing; using System; -using System.Linq; -using System.Collections; -using System.Collections.Generic; using Serilog.PerformanceTests.Support; -using Xunit; namespace Serilog.PerformanceTests { diff --git a/test/Serilog.PerformanceTests/Support/Some.cs b/test/Serilog.PerformanceTests/Support/Some.cs index 07fbf5aab..5c0287224 100644 --- a/test/Serilog.PerformanceTests/Support/Some.cs +++ b/test/Serilog.PerformanceTests/Support/Some.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Threading; using Serilog.Events; using Serilog.Parsing; diff --git a/test/Serilog.Tests/Core/LogEventPropertyCapturingTests.cs b/test/Serilog.Tests/Core/LogEventPropertyCapturingTests.cs index 07c19f093..8e9f05837 100644 --- a/test/Serilog.Tests/Core/LogEventPropertyCapturingTests.cs +++ b/test/Serilog.Tests/Core/LogEventPropertyCapturingTests.cs @@ -1,10 +1,10 @@ using System; using System.Linq; using System.Collections.Generic; +using Serilog.Capturing; using Serilog.Core; using Serilog.Debugging; using Serilog.Events; -using Serilog.Parameters; using Serilog.Parsing; using Serilog.Tests.Support; using Xunit; diff --git a/test/Serilog.Tests/Core/MessageTemplateTests.cs b/test/Serilog.Tests/Core/MessageTemplateTests.cs index b76185d36..8ae5cdb33 100755 --- a/test/Serilog.Tests/Core/MessageTemplateTests.cs +++ b/test/Serilog.Tests/Core/MessageTemplateTests.cs @@ -3,9 +3,9 @@ using System.IO; using System.Linq; using System.Text; +using Serilog.Capturing; using Xunit; using Serilog.Core; -using Serilog.Parameters; using MessageTemplateParser = Serilog.Parsing.MessageTemplateParser; namespace Serilog.Tests.Core diff --git a/test/Serilog.Tests/Core/SecondaryLoggerSinkTests.cs b/test/Serilog.Tests/Core/SecondaryLoggerSinkTests.cs index 0408235ed..c84007bf7 100644 --- a/test/Serilog.Tests/Core/SecondaryLoggerSinkTests.cs +++ b/test/Serilog.Tests/Core/SecondaryLoggerSinkTests.cs @@ -1,5 +1,4 @@ -using System; -using Serilog.Tests.Support; +using Serilog.Tests.Support; using Xunit; using Serilog.Core; using Serilog.Events; @@ -37,9 +36,10 @@ public void WhenOwnedByCallerSecondaryLoggerIsNotDisposed() .WriteTo.Sink(secondary) .CreateLogger(); - ((IDisposable)new LoggerConfiguration() + new LoggerConfiguration() .WriteTo.Logger(secondaryLogger) - .CreateLogger()).Dispose(); + .CreateLogger() + .Dispose(); Assert.False(secondary.IsDisposed); } @@ -49,9 +49,10 @@ public void WhenOwnedByPrimaryLoggerSecondaryIsDisposed() { var secondary = new DisposeTrackingSink(); - ((IDisposable)new LoggerConfiguration() + new LoggerConfiguration() .WriteTo.Logger(lc => lc.WriteTo.Sink(secondary)) - .CreateLogger()).Dispose(); + .CreateLogger() + .Dispose(); Assert.True(secondary.IsDisposed); } diff --git a/test/Serilog.Tests/Events/LogEventPropertyValueTests.cs b/test/Serilog.Tests/Events/LogEventPropertyValueTests.cs index 534351075..76d9dfe9f 100644 --- a/test/Serilog.Tests/Events/LogEventPropertyValueTests.cs +++ b/test/Serilog.Tests/Events/LogEventPropertyValueTests.cs @@ -15,10 +15,10 @@ using System; using System.Globalization; using System.Linq; +using Serilog.Capturing; using Xunit; using Serilog.Core; using Serilog.Events; -using Serilog.Parameters; using Serilog.Parsing; using Serilog.Tests.Support; diff --git a/test/Serilog.Tests/Formatting/Json/JsonValueFormatterTests.cs b/test/Serilog.Tests/Formatting/Json/JsonValueFormatterTests.cs index 45e22324a..84aca1af5 100644 --- a/test/Serilog.Tests/Formatting/Json/JsonValueFormatterTests.cs +++ b/test/Serilog.Tests/Formatting/Json/JsonValueFormatterTests.cs @@ -1,11 +1,8 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using Serilog.Events; using Serilog.Formatting.Json; -using Serilog.Tests.Events; -using Serilog.Tests.Support; using Xunit; namespace Serilog.Tests.Formatting.Json diff --git a/test/Serilog.Tests/LoggerConfigurationTests.cs b/test/Serilog.Tests/LoggerConfigurationTests.cs index c757a2dfd..55f05d105 100644 --- a/test/Serilog.Tests/LoggerConfigurationTests.cs +++ b/test/Serilog.Tests/LoggerConfigurationTests.cs @@ -1,11 +1,8 @@ using System; -using System.IO; using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Runtime.InteropServices.ComTypes; using Xunit; -using Serilog.Configuration; using Serilog.Core; using Serilog.Core.Filters; using Serilog.Debugging; @@ -120,7 +117,7 @@ public void DestructuringSystemTypeGivesScalarByDefault() .WriteTo.Sink(sink) .CreateLogger(); - var thisType = this.GetType(); + var thisType = GetType(); logger.Information("{@thisType}", thisType); var ev = events.Single(); @@ -171,7 +168,7 @@ public void DestructuringIsPossibleForSystemTypeDerivedProperties() .WriteTo.Sink(sink) .CreateLogger(); - var thisType = this.GetType(); + var thisType = GetType(); logger.Information("{@thisType}", thisType); var ev = events.Single(); @@ -495,7 +492,7 @@ public void HigherMinimumLevelOverridesArePropagated() .CreateLogger(); logger.Write(Some.InformationEvent()); - logger.ForContext(Serilog.Core.Constants.SourceContextPropertyName, "Microsoft.AspNet.Something").Write(Some.InformationEvent()); + logger.ForContext(Constants.SourceContextPropertyName, "Microsoft.AspNet.Something").Write(Some.InformationEvent()); logger.ForContext().Write(Some.InformationEvent()); Assert.Equal(2, sink.Events.Count); @@ -513,7 +510,7 @@ public void LowerMinimumLevelOverridesArePropagated() .CreateLogger(); logger.Write(Some.InformationEvent()); - logger.ForContext(Serilog.Core.Constants.SourceContextPropertyName, "Microsoft.AspNet.Something").Write(Some.InformationEvent()); + logger.ForContext(Constants.SourceContextPropertyName, "Microsoft.AspNet.Something").Write(Some.InformationEvent()); logger.ForContext().Write(Some.InformationEvent()); Assert.Equal(1, sink.Events.Count); @@ -523,7 +520,7 @@ public void LowerMinimumLevelOverridesArePropagated() public void ExceptionsThrownBySinksAreNotPropagated() { var logger = new LoggerConfiguration() - .WriteTo.Sink(new DelegatingSink(e => { throw new Exception("Boom!"); })) + .WriteTo.Sink(new DelegatingSink(e => throw new Exception("Boom!"))) .CreateLogger(); logger.Write(Some.InformationEvent()); diff --git a/test/Serilog.Tests/MethodOverloadConventionTests.cs b/test/Serilog.Tests/MethodOverloadConventionTests.cs index c634f46e0..1c03f55f6 100644 --- a/test/Serilog.Tests/MethodOverloadConventionTests.cs +++ b/test/Serilog.Tests/MethodOverloadConventionTests.cs @@ -10,6 +10,10 @@ using System.Text.RegularExpressions; using Xunit; using Xunit.Sdk; +// ReSharper disable PossibleMultipleEnumeration +// ReSharper disable UnusedMember.Local +// ReSharper disable UnusedParameter.Local +// ReSharper disable AssignNullToNotNullAttribute namespace Serilog.Tests { @@ -97,10 +101,9 @@ public void ValidateWriteEventLogMethods(Type loggerType) Assert.True(writeMethod.IsPublic); Assert.Equal(writeMethod.ReturnType, typeof(void)); - LogEventLevel level = LogEventLevel.Information; + var level = LogEventLevel.Information; CollectingSink sink; - var logger = GetLogger(loggerType, out sink); InvokeMethod(writeMethod, logger, new object[] { Some.LogEvent(DateTimeOffset.Now, level) }); @@ -108,8 +111,8 @@ public void ValidateWriteEventLogMethods(Type loggerType) //handle silent logger special case i.e. no result validation if (loggerType == typeof(SilentLogger)) return; - else - EvaluateSingleResult(level, sink); + + EvaluateSingleResult(level, sink); } [Theory] @@ -158,12 +161,10 @@ public void ValidateForContextMethods(Type loggerType) { report.AppendLine($"{testMethod.Name} Invocation Failure on: {method} with: {xunitException.UserMessage}"); } - - continue; } } - Assert.True(signatureMatchAndInvokeSuccess, $"{method} did not match any known method or failed invoke\n" + report.ToString()); + Assert.True(signatureMatchAndInvokeSuccess, $"{method} did not match any known method or failed invoke\n" + report); } } @@ -185,7 +186,7 @@ public void ValidateBindMessageTemplateMethods(Type loggerType) Assert.Equal(messageTemplateAttr.MessageTemplateParameterName, MessageTemplate); var parameters = method.GetParameters(); - int index = 0; + var index = 0; Assert.Equal(parameters[index].Name, "messageTemplate"); Assert.Equal(parameters[index].ParameterType, typeof(string)); @@ -203,7 +204,6 @@ public void ValidateBindMessageTemplateMethods(Type loggerType) Assert.Equal(parameters[index].Name, "boundProperties"); Assert.Equal(parameters[index].ParameterType, typeof(IEnumerable).MakeByRefType()); Assert.True(parameters[index].IsOut); - index++; var logger = GetLogger(loggerType); @@ -242,7 +242,7 @@ public void ValidateBindPropertyMethods(Type loggerType) Assert.True(method.IsPublic); var parameters = method.GetParameters(); - int index = 0; + var index = 0; Assert.Equal(parameters[index].Name, "propertyName"); Assert.Equal(parameters[index].ParameterType, typeof(string)); @@ -343,7 +343,7 @@ void ForContextMethod0(MethodInfo method) { e.Data.Add("IsSignatureAssertionFailure", true); - throw e; + throw; } var logger = GetLogger(method.DeclaringType); @@ -375,7 +375,7 @@ void ForContextMethod1(MethodInfo method) { e.Data.Add("IsSignatureAssertionFailure", true); - throw e; + throw; } var logger = GetLogger(method.DeclaringType); @@ -396,7 +396,7 @@ void ForContextMethod2(MethodInfo method) var parameters = method.GetParameters(); Assert.Equal(parameters.Length, 3); - int index = 0; + var index = 0; Assert.Equal(parameters[index].Name, "propertyName"); Assert.Equal(parameters[index].ParameterType, typeof(string)); @@ -414,7 +414,7 @@ void ForContextMethod2(MethodInfo method) { e.Data.Add("IsSignatureAssertionFailure", true); - throw e; + throw; } var logger = GetLogger(method.DeclaringType); @@ -464,12 +464,12 @@ void ForContextMethod3(MethodInfo method) { e.Data.Add("IsSignatureAssertionFailure", true); - throw e; + throw; } var logger = GetLogger(method.DeclaringType); - var enrichedLogger = InvokeMethod(method, logger, null, new Type[] { typeof(object) }); + var enrichedLogger = InvokeMethod(method, logger, null, new[] { typeof(object) }); Assert.NotNull(enrichedLogger); Assert.True(enrichedLogger is ILogger); @@ -493,7 +493,7 @@ void ForContextMethod4(MethodInfo method) { e.Data.Add("IsSignatureAssertionFailure", true); - throw e; + throw; } var logger = GetLogger(method.DeclaringType); @@ -566,7 +566,7 @@ void ValidateConventionForMethodSet( { try { - Action invokeTestMethod = null; + Action invokeTestMethod; if (testInvokeResults) invokeTestMethod = InvokeConventionMethodAndTest; @@ -592,19 +592,17 @@ void ValidateConventionForMethodSet( { report.AppendLine($"{testMethod.Name} Invocation Failure on: {method} with: {xunitException.UserMessage}"); } - - continue; } } - Assert.True(signatureMatchAndInvokeSuccess, $"{method} did not match any known convention or failed invoke\n" + report.ToString()); + Assert.True(signatureMatchAndInvokeSuccess, $"{method} did not match any known convention or failed invoke\n" + report); } } // Method0 (string messageTemplate) : void void ValidateMethod0(MethodInfo method, Action invokeMethod) { - VerifyMethodSignature(method, expectedArgCount: 1); + VerifyMethodSignature(method); var parameters = new object[] { "message" }; @@ -616,7 +614,7 @@ void ValidateMethod1(MethodInfo method, Action inv { VerifyMethodSignature(method, isGeneric: true, expectedArgCount: 2); - var typeArgs = new Type[] { typeof(string) }; + var typeArgs = new[] { typeof(string) }; var parameters = new object[] { "message", "value0" }; @@ -628,7 +626,7 @@ void ValidateMethod2(MethodInfo method, Action inv { VerifyMethodSignature(method, isGeneric: true, expectedArgCount: 3); - var typeArgs = new Type[] { typeof(string), typeof(string) }; + var typeArgs = new[] { typeof(string), typeof(string) }; var parameters = new object[] { @@ -643,7 +641,7 @@ void ValidateMethod3(MethodInfo method, Action inv { VerifyMethodSignature(method, isGeneric: true, expectedArgCount: 4); - var typeArgs = new Type[] { typeof(string), typeof(string), typeof(string) }; + var typeArgs = new[] { typeof(string), typeof(string), typeof(string) }; var parameters = new object[] { @@ -681,7 +679,7 @@ void ValidateMethod6(MethodInfo method, Action inv { VerifyMethodSignature(method, hasExceptionArg: true, isGeneric: true, expectedArgCount: 3); - var typeArgs = new Type[] { typeof(string) }; + var typeArgs = new[] { typeof(string) }; var parameters = new object[] { @@ -696,7 +694,7 @@ void ValidateMethod7(MethodInfo method, Action inv { VerifyMethodSignature(method, hasExceptionArg: true, isGeneric: true, expectedArgCount: 4); - var typeArgs = new Type[] { typeof(string), typeof(string) }; + var typeArgs = new[] { typeof(string), typeof(string) }; var parameters = new object[] { @@ -711,7 +709,7 @@ void ValidateMethod8(MethodInfo method, Action inv { VerifyMethodSignature(method, hasExceptionArg: true, isGeneric: true, expectedArgCount: 5); - var typeArgs = new Type[] { typeof(string), typeof(string), typeof(string) }; + var typeArgs = new[] { typeof(string), typeof(string), typeof(string) }; var parameters = new object[] { @@ -726,7 +724,7 @@ void ValidateMethod9(MethodInfo method, Action inv { VerifyMethodSignature(method, hasExceptionArg: true, expectedArgCount: 3); - object[] parameters = new object[] + var parameters = new object[] { new Exception("test"), "Processed {value0}, {value1}, {value2}", new object[] { "value0", "value1", "value2" } @@ -786,7 +784,7 @@ static void VerifyMethodSignature(MethodInfo method, bool hasExceptionArg = fals { var parameters = method.GetParameters(); - int index = 0; + var index = 0; if (method.Name == Write) { @@ -822,7 +820,7 @@ static void VerifyMethodSignature(MethodInfo method, bool hasExceptionArg = fals //multiple generic argument convention T0...Tx : T0 propertyValue0... Tx propertyValueX if (genericTypeArgs.Length > 1) { - for (int i = 0; i < genericTypeArgs.Length; i++, index++) + for (var i = 0; i < genericTypeArgs.Length; i++, index++) { Assert.Equal(genericTypeArgs[i].Name, $"T{i}"); @@ -867,7 +865,7 @@ static void VerifyMethodSignature(MethodInfo method, bool hasExceptionArg = fals // mark xunit assertion failures e.Data.Add("IsSignatureAssertionFailure", true); - throw e; + throw; } } @@ -881,13 +879,14 @@ static object InvokeMethod( { if (method.IsGenericMethod) return method.MakeGenericMethod(typeArgs).Invoke(null, parameters); - else - return method.Invoke(null, parameters); + + return method.Invoke(null, parameters); } - else if (method.IsGenericMethod) + + if (method.IsGenericMethod) return method.MakeGenericMethod(typeArgs).Invoke(instance, parameters); - else - return method.Invoke(instance, parameters); + + return method.Invoke(instance, parameters); } static void EvaluateSingleResult(LogEventLevel level, CollectingSink results) @@ -920,7 +919,8 @@ static ILogger GetLogger(Type loggerType, out CollectingSink sink, LogEventLevel .WriteTo.Sink(sink) .CreateLogger(); } - else if (loggerType == typeof(Log)) + + if (loggerType == typeof(Log)) { sink = new CollectingSink(); @@ -933,10 +933,11 @@ static ILogger GetLogger(Type loggerType, out CollectingSink sink, LogEventLevel return null; } - else if (loggerType == typeof(SilentLogger)) + + if (loggerType == typeof(SilentLogger)) return new SilentLogger(); - else - throw new ArgumentException($"Logger Type of {loggerType} is not supported"); + + throw new ArgumentException($"Logger Type of {loggerType} is not supported"); } } } \ No newline at end of file diff --git a/test/Serilog.Tests/Parameters/PropertyValueConverterTests.cs b/test/Serilog.Tests/Parameters/PropertyValueConverterTests.cs index 025f1e979..6d3a47357 100644 --- a/test/Serilog.Tests/Parameters/PropertyValueConverterTests.cs +++ b/test/Serilog.Tests/Parameters/PropertyValueConverterTests.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; +using Serilog.Capturing; using Xunit; using Serilog.Core; using Serilog.Events; -using Serilog.Parameters; using Serilog.Parsing; using Serilog.Tests.Support; diff --git a/test/Serilog.Tests/Settings/KeyValuePairSettingsTests.cs b/test/Serilog.Tests/Settings/KeyValuePairSettingsTests.cs index ffb974378..9725f0374 100644 --- a/test/Serilog.Tests/Settings/KeyValuePairSettingsTests.cs +++ b/test/Serilog.Tests/Settings/KeyValuePairSettingsTests.cs @@ -6,12 +6,9 @@ using Serilog.Events; using Serilog.Settings.KeyValuePairs; using Serilog.Tests.Support; -using Serilog.Enrichers; using TestDummies; using Serilog.Configuration; using Serilog.Formatting; -using Serilog.Formatting.Json; -using Serilog.Tests.Formatting.Json; namespace Serilog.Tests.Settings { @@ -142,7 +139,7 @@ public void TestMinimumLevelOverrides() .WriteTo.Sink(new DelegatingSink(e => evt = e)) .CreateLogger(); - var systemLogger = log.ForContext(); + var systemLogger = log.ForContext(); systemLogger.Write(Some.InformationEvent()); Assert.Null(evt); diff --git a/test/Serilog.Tests/Settings/SettingValueConversionsTests.cs b/test/Serilog.Tests/Settings/SettingValueConversionsTests.cs index 0fca5ec32..3ee1bc118 100644 --- a/test/Serilog.Tests/Settings/SettingValueConversionsTests.cs +++ b/test/Serilog.Tests/Settings/SettingValueConversionsTests.cs @@ -46,7 +46,7 @@ public void ValuesConvertToEnumMembers() [Fact] public void StringValuesConvertToDefaultInstancesIfTargetIsInterface() { - var result = (object)SettingValueConversions.ConvertToType("Serilog.Formatting.Json.JsonFormatter", typeof(ITextFormatter)); + var result = SettingValueConversions.ConvertToType("Serilog.Formatting.Json.JsonFormatter", typeof(ITextFormatter)); Assert.IsType(result); } } diff --git a/test/Serilog.Tests/Support/DisposableLogger.cs b/test/Serilog.Tests/Support/DisposableLogger.cs index 1c46e6132..aa465c1d1 100644 --- a/test/Serilog.Tests/Support/DisposableLogger.cs +++ b/test/Serilog.Tests/Support/DisposableLogger.cs @@ -5,7 +5,7 @@ namespace Serilog.Tests.Support { - public class DisposableLogger : Serilog.ILogger, IDisposable + public class DisposableLogger : ILogger, IDisposable { public bool Disposed { get; set; } diff --git a/test/Serilog.Tests/Support/LogEventPropertyStructuralEqualityComparer.cs b/test/Serilog.Tests/Support/LogEventPropertyStructuralEqualityComparer.cs index e1162ed8f..8b6135bd6 100644 --- a/test/Serilog.Tests/Support/LogEventPropertyStructuralEqualityComparer.cs +++ b/test/Serilog.Tests/Support/LogEventPropertyStructuralEqualityComparer.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using Serilog.Events; @@ -11,7 +10,7 @@ class LogEventPropertyStructuralEqualityComparer : IEqualityComparer valueEqualityComparer = null) { - this._valueEqualityComparer = + _valueEqualityComparer = valueEqualityComparer ?? new LogEventPropertyValueComparer(EqualityComparer.Default); } diff --git a/test/Serilog.Tests/Support/LogEventPropertyValueComparer.cs b/test/Serilog.Tests/Support/LogEventPropertyValueComparer.cs index 232b1cf96..b6e57d7f1 100644 --- a/test/Serilog.Tests/Support/LogEventPropertyValueComparer.cs +++ b/test/Serilog.Tests/Support/LogEventPropertyValueComparer.cs @@ -11,7 +11,7 @@ class LogEventPropertyValueComparer : IEqualityComparer public LogEventPropertyValueComparer(IEqualityComparer objectEqualityComparer = null) { - this._objectEqualityComparer = objectEqualityComparer ?? EqualityComparer.Default; + _objectEqualityComparer = objectEqualityComparer ?? EqualityComparer.Default; } public bool Equals(LogEventPropertyValue x, LogEventPropertyValue y) diff --git a/test/Serilog.Tests/Support/StringSink.cs b/test/Serilog.Tests/Support/StringSink.cs index e7102af69..5bb088467 100644 --- a/test/Serilog.Tests/Support/StringSink.cs +++ b/test/Serilog.Tests/Support/StringSink.cs @@ -1,5 +1,4 @@ -using System; -using System.Globalization; +using System.Globalization; using System.IO; using Serilog.Core; using Serilog.Events; diff --git a/test/TestDummies/DummyThreadIdEnricher.cs b/test/TestDummies/DummyThreadIdEnricher.cs index 142c15282..4ba740eb7 100644 --- a/test/TestDummies/DummyThreadIdEnricher.cs +++ b/test/TestDummies/DummyThreadIdEnricher.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Serilog.Core; +using Serilog.Core; using Serilog.Events; namespace TestDummies diff --git a/test/TestDummies/Properties/AssemblyInfo.cs b/test/TestDummies/Properties/AssemblyInfo.cs index 1f1fc4f85..89032973b 100644 --- a/test/TestDummies/Properties/AssemblyInfo.cs +++ b/test/TestDummies/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following From 2ce90e416bb3483e8d64e8b9bf5e844d7df921e1 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 1 Jun 2017 20:39:30 +1000 Subject: [PATCH 06/12] Improve flow of MessageTemplateRenderer --- .../Formatting/Display/LevelOutputFormat.cs | 1 + .../Display/MessageTemplateTextFormatter.cs | 84 +++++++------------ .../Display/Obsolete/LiteralStringValue.cs | 1 + src/Serilog/Parsing/TextToken.cs | 3 +- .../Display => Rendering}/Casing.cs | 2 +- .../Rendering/MessageTemplateRenderer.cs | 10 ++- .../Display => Rendering}/Padding.cs | 3 +- 7 files changed, 45 insertions(+), 59 deletions(-) rename src/Serilog/{Formatting/Display => Rendering}/Casing.cs (97%) rename src/Serilog/{Formatting/Display => Rendering}/Padding.cs (97%) diff --git a/src/Serilog/Formatting/Display/LevelOutputFormat.cs b/src/Serilog/Formatting/Display/LevelOutputFormat.cs index 8e3977c70..76f9b3d5e 100644 --- a/src/Serilog/Formatting/Display/LevelOutputFormat.cs +++ b/src/Serilog/Formatting/Display/LevelOutputFormat.cs @@ -13,6 +13,7 @@ // limitations under the License. using Serilog.Events; +using Serilog.Rendering; namespace Serilog.Formatting.Display { diff --git a/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs b/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs index 5295d7af1..3ce7c27f9 100644 --- a/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs +++ b/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs @@ -70,34 +70,7 @@ public void Format(LogEvent logEvent, TextWriter output) } var pt = (PropertyToken)token; - - if (pt.PropertyName == OutputProperties.MessagePropertyName) - { - if (pt.Alignment.HasValue) - { - var sw = new StringWriter(); - MessageTemplateRenderer.Render(logEvent.MessageTemplate, logEvent.Properties, sw, pt.Format, _formatProvider); - Padding.Apply(output, sw.ToString(), pt.Alignment); - } - else - { - MessageTemplateRenderer.Render(logEvent.MessageTemplate, logEvent.Properties, output, pt.Format, _formatProvider); - } - } - else if (pt.PropertyName == OutputProperties.TimestampPropertyName) - { - if (pt.Alignment.HasValue) - { - var sw = new StringWriter(); - ScalarValue.Render(logEvent.Timestamp, sw, pt.Format, _formatProvider); - Padding.Apply(output, sw.ToString(), pt.Alignment); - } - else - { - ScalarValue.Render(logEvent.Timestamp, output, pt.Format, _formatProvider); - } - } - else if (pt.PropertyName == OutputProperties.LevelPropertyName) + if (pt.PropertyName == OutputProperties.LevelPropertyName) { var moniker = LevelOutputFormat.GetLevelMoniker(logEvent.Level, pt.Format); Padding.Apply(output, moniker, pt.Alignment); @@ -111,40 +84,47 @@ public void Format(LogEvent logEvent, TextWriter output) var exception = logEvent.Exception == null ? "" : logEvent.Exception + Environment.NewLine; Padding.Apply(output, exception, pt.Alignment); } - else if (pt.PropertyName == OutputProperties.PropertiesPropertyName) + else { - if (pt.Alignment.HasValue) + // In this block, `writer` may be used to buffer output so that + // padding can be applied. + var writer = pt.Alignment.HasValue ? new StringWriter() : output; + + if (pt.PropertyName == OutputProperties.MessagePropertyName) { - var sw = new StringWriter(); - PropertiesOutputFormat.Render(logEvent.MessageTemplate, logEvent.Properties, _outputTemplate, sw, _formatProvider); - Padding.Apply(output, sw.ToString(), pt.Alignment); + MessageTemplateRenderer.Render(logEvent.MessageTemplate, logEvent.Properties, writer, pt.Format, _formatProvider); } - else + else if (pt.PropertyName == OutputProperties.TimestampPropertyName) { - PropertiesOutputFormat.Render(logEvent.MessageTemplate, logEvent.Properties, _outputTemplate, output, _formatProvider); + ScalarValue.Render(logEvent.Timestamp, writer, pt.Format, _formatProvider); } - } - else - { - // First variation from normal rendering - if a property is missing, - // don't render anything (message templates render the raw token here). - LogEventPropertyValue propertyValue; - if (!logEvent.Properties.TryGetValue(pt.PropertyName, out propertyValue)) - continue; - - // Second variation; if the value is a scalar string, use literal - // rendering and support some additional formats: 'u' for uppercase - // and 'w' for lowercase. - var sv = propertyValue as ScalarValue; - if (sv?.Value is string literalString) + else if (pt.PropertyName == OutputProperties.PropertiesPropertyName) { - var cased = Casing.Format(literalString, pt.Format); - Padding.Apply(output, cased, pt.Alignment); + PropertiesOutputFormat.Render(logEvent.MessageTemplate, logEvent.Properties, _outputTemplate, writer, _formatProvider); } else { - token.Render(logEvent.Properties, output, _formatProvider); + // If a property is missing, don't render anything (message templates render the raw token here). + LogEventPropertyValue propertyValue; + if (!logEvent.Properties.TryGetValue(pt.PropertyName, out propertyValue)) + continue; + + // If the value is a scalar string, support some additional formats: 'u' for uppercase + // and 'w' for lowercase. + var sv = propertyValue as ScalarValue; + if (sv?.Value is string literalString) + { + var cased = Casing.Format(literalString, pt.Format); + writer.Write(cased); + } + else + { + propertyValue.Render(output, pt.Format, _formatProvider); + } } + + if (pt.Alignment.HasValue) + Padding.Apply(output, ((StringWriter)writer).ToString(), pt.Alignment); } } } diff --git a/src/Serilog/Formatting/Display/Obsolete/LiteralStringValue.cs b/src/Serilog/Formatting/Display/Obsolete/LiteralStringValue.cs index f537c5e24..e7bc44023 100644 --- a/src/Serilog/Formatting/Display/Obsolete/LiteralStringValue.cs +++ b/src/Serilog/Formatting/Display/Obsolete/LiteralStringValue.cs @@ -15,6 +15,7 @@ using System; using System.IO; using Serilog.Events; +using Serilog.Rendering; namespace Serilog.Formatting.Display.Obsolete { diff --git a/src/Serilog/Parsing/TextToken.cs b/src/Serilog/Parsing/TextToken.cs index daf1531ca..974e5f530 100644 --- a/src/Serilog/Parsing/TextToken.cs +++ b/src/Serilog/Parsing/TextToken.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using System.IO; using Serilog.Events; +using Serilog.Rendering; namespace Serilog.Parsing { @@ -49,7 +50,7 @@ public TextToken(string text, int startIndex = -1) : base(startIndex) public override void Render(IReadOnlyDictionary properties, TextWriter output, IFormatProvider formatProvider = null) { if (output == null) throw new ArgumentNullException(nameof(output)); - output.Write(Text); + MessageTemplateRenderer.RenderTextToken(this, output); } /// diff --git a/src/Serilog/Formatting/Display/Casing.cs b/src/Serilog/Rendering/Casing.cs similarity index 97% rename from src/Serilog/Formatting/Display/Casing.cs rename to src/Serilog/Rendering/Casing.cs index a4e717278..62aa022f5 100644 --- a/src/Serilog/Formatting/Display/Casing.cs +++ b/src/Serilog/Rendering/Casing.cs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -namespace Serilog.Formatting.Display +namespace Serilog.Rendering { static class Casing { diff --git a/src/Serilog/Rendering/MessageTemplateRenderer.cs b/src/Serilog/Rendering/MessageTemplateRenderer.cs index 446e52556..e4584b924 100644 --- a/src/Serilog/Rendering/MessageTemplateRenderer.cs +++ b/src/Serilog/Rendering/MessageTemplateRenderer.cs @@ -1,4 +1,4 @@ -// Copyright 2013-2015 Serilog Contributors +// Copyright 2013-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. @@ -16,7 +16,6 @@ using System.Collections.Generic; using System.IO; using Serilog.Events; -using Serilog.Formatting.Display; using Serilog.Formatting.Json; using Serilog.Parsing; @@ -44,7 +43,7 @@ public static void Render(MessageTemplate messageTemplate, IReadOnlyDictionary properties, TextWriter output, IFormatProvider formatProvider, bool isLiteral, bool isJson) { LogEventPropertyValue propertyValue; diff --git a/src/Serilog/Formatting/Display/Padding.cs b/src/Serilog/Rendering/Padding.cs similarity index 97% rename from src/Serilog/Formatting/Display/Padding.cs rename to src/Serilog/Rendering/Padding.cs index bd0e059fc..23ea8206e 100644 --- a/src/Serilog/Formatting/Display/Padding.cs +++ b/src/Serilog/Rendering/Padding.cs @@ -13,10 +13,9 @@ // limitations under the License. using System.IO; - using Serilog.Parsing; -namespace Serilog.Formatting.Display +namespace Serilog.Rendering { static class Padding { From 3ba9d2a1669133b7535fa34b90282b3408bb5d51 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 1 Jun 2017 21:17:02 +1000 Subject: [PATCH 07/12] Tests for {Message:l} and {Message:j} formatting --- src/Serilog/Formatting/Raw/RawFormatter.cs | 2 ++ src/Serilog/Parsing/MessageTemplateToken.cs | 1 + .../Rendering/MessageTemplateRenderer.cs | 14 ++++++--- .../PropertyValueConverterTests.cs | 4 +-- .../MessageTemplateTextFormatterTests.cs | 29 +++++++++++++++++++ 5 files changed, 44 insertions(+), 6 deletions(-) rename test/Serilog.Tests/{Parameters => Capturing}/PropertyValueConverterTests.cs (99%) diff --git a/src/Serilog/Formatting/Raw/RawFormatter.cs b/src/Serilog/Formatting/Raw/RawFormatter.cs index 95ad05e3b..6138c190e 100644 --- a/src/Serilog/Formatting/Raw/RawFormatter.cs +++ b/src/Serilog/Formatting/Raw/RawFormatter.cs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System; using System.IO; using Serilog.Events; @@ -20,6 +21,7 @@ namespace Serilog.Formatting.Raw /// /// Formats log events as a raw dump of the message template and properties. /// + [Obsolete("A JSON-based formatter such as `Serilog.Formatting.Compact.CompactJsonFormatter` is recommended for this task.")] public class RawFormatter : ITextFormatter { /// diff --git a/src/Serilog/Parsing/MessageTemplateToken.cs b/src/Serilog/Parsing/MessageTemplateToken.cs index 52a3abcb2..a7cc83ba1 100644 --- a/src/Serilog/Parsing/MessageTemplateToken.cs +++ b/src/Serilog/Parsing/MessageTemplateToken.cs @@ -50,6 +50,7 @@ protected MessageTemplateToken(int startIndex) /// Properties that may be represented by the token. /// Output for the rendered string. /// Supplies culture-specific formatting information, or null. + // ReSharper disable once UnusedMemberInSuper.Global public abstract void Render(IReadOnlyDictionary properties, TextWriter output, IFormatProvider formatProvider = null); } } \ No newline at end of file diff --git a/src/Serilog/Rendering/MessageTemplateRenderer.cs b/src/Serilog/Rendering/MessageTemplateRenderer.cs index e4584b924..7da313f09 100644 --- a/src/Serilog/Rendering/MessageTemplateRenderer.cs +++ b/src/Serilog/Rendering/MessageTemplateRenderer.cs @@ -33,14 +33,20 @@ public static void Render(MessageTemplate messageTemplate, IReadOnlyDictionary l.Information("Hello, {Name}!", "World")); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal(expected, sw.ToString()); + } + + [Theory] + [InlineData("", "{ Name: \"World\" }")] + [InlineData(":j", "{\"Name\":\"World\"}")] + [InlineData(":lj", "{\"Name\":\"World\"}")] + [InlineData(":jl", "{\"Name\":\"World\"}")] + public void AppliesJsonFormattingToMessageStructuresWhenSpecified(string format, string expected) + { + var formatter = new MessageTemplateTextFormatter("{Message" + format + "}", null); + var evt = DelegatingSink.GetLogEvent(l => l.Information("{@Obj}", new {Name = "World"})); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal(expected, sw.ToString()); + } } } From 5f5770d298c6fc4872b2d2eed0250eda8c342b41 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 2 Jun 2017 08:43:48 +1000 Subject: [PATCH 08/12] Render text token through the external method --- .../Formatting/Display/MessageTemplateTextFormatter.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs b/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs index 3ce7c27f9..ac45223db 100644 --- a/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs +++ b/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs @@ -13,7 +13,6 @@ // limitations under the License. using System; -using System.Collections.Generic; using System.IO; using Serilog.Events; using Serilog.Parsing; @@ -36,8 +35,6 @@ public class MessageTemplateTextFormatter : ITextFormatter readonly IFormatProvider _formatProvider; readonly MessageTemplate _outputTemplate; - static readonly IReadOnlyDictionary NoProperties = new Dictionary(); - /// /// Construct a . /// @@ -65,7 +62,7 @@ public void Format(LogEvent logEvent, TextWriter output) { if (token is TextToken tt) { - tt.Render(NoProperties, output, _formatProvider); + MessageTemplateRenderer.RenderTextToken(tt, output); continue; } From 0308bc702686d886b4a00738dc6acacc0cf86064 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Mon, 5 Jun 2017 08:37:46 +1000 Subject: [PATCH 09/12] Update BenchmarkDotNet to 0.10.6 --- .../Serilog.PerformanceTests.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Serilog.PerformanceTests/Serilog.PerformanceTests.csproj b/test/Serilog.PerformanceTests/Serilog.PerformanceTests.csproj index 9b54fe0d9..7853b5859 100644 --- a/test/Serilog.PerformanceTests/Serilog.PerformanceTests.csproj +++ b/test/Serilog.PerformanceTests/Serilog.PerformanceTests.csproj @@ -1,6 +1,6 @@  - netcoreapp1.1;net452 + netcoreapp1.1;net46 Serilog.PerformanceTests ../../assets/Serilog.snk true @@ -18,11 +18,11 @@ - + - + From 4b0e2bef32c482f3d2ee5fc4f36ac707298772e4 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Mon, 5 Jun 2017 08:50:45 +1000 Subject: [PATCH 10/12] Fix some dodgy string formatting benchmarks --- .../MessageTemplateRenderingBenchmark.cs | 9 +- .../OutputTemplateRenderingBenchmark.cs | 9 +- .../Support/NullTextWriter.cs | 160 ++++++++++++++++++ 3 files changed, 162 insertions(+), 16 deletions(-) create mode 100644 test/Serilog.PerformanceTests/Support/NullTextWriter.cs diff --git a/test/Serilog.PerformanceTests/MessageTemplateRenderingBenchmark.cs b/test/Serilog.PerformanceTests/MessageTemplateRenderingBenchmark.cs index 76023f421..125e2a512 100644 --- a/test/Serilog.PerformanceTests/MessageTemplateRenderingBenchmark.cs +++ b/test/Serilog.PerformanceTests/MessageTemplateRenderingBenchmark.cs @@ -17,14 +17,7 @@ public class MessageTemplateRenderingBenchmark Some.InformationEvent("Processed {@Position} for {Task} in {Elapsed:000} ms", new { Latitude = 25, Longitude = 134 }, "Benchmark", 34); - readonly StringWriter _output = new StringWriter(); - - [Setup] - public void Setup() - { - _output.GetStringBuilder().Length = 0; - _output.GetStringBuilder().Capacity = 1024; // Only a few dozen chars actually needed here. - } + readonly TextWriter _output = new NullTextWriter(); [Benchmark] public void TemplateWithNoProperties() diff --git a/test/Serilog.PerformanceTests/OutputTemplateRenderingBenchmark.cs b/test/Serilog.PerformanceTests/OutputTemplateRenderingBenchmark.cs index cae6a892d..17df8db72 100644 --- a/test/Serilog.PerformanceTests/OutputTemplateRenderingBenchmark.cs +++ b/test/Serilog.PerformanceTests/OutputTemplateRenderingBenchmark.cs @@ -17,14 +17,7 @@ public class OutputTemplateRenderingBenchmark static readonly LogEvent HelloWorldEvent = Some.InformationEvent("Hello, {Name}", "World"); static readonly MessageTemplateTextFormatter Formatter = new MessageTemplateTextFormatter(DefaultFileOutputTemplate, CultureInfo.InvariantCulture); - readonly StringWriter _output = new StringWriter(); - - [Setup] - public void Setup() - { - _output.GetStringBuilder().Length = 0; - _output.GetStringBuilder().Capacity = 1024; // Only a few dozen chars actually needed here. - } + readonly TextWriter _output = new NullTextWriter(); [Benchmark] public void FormatToOutput() diff --git a/test/Serilog.PerformanceTests/Support/NullTextWriter.cs b/test/Serilog.PerformanceTests/Support/NullTextWriter.cs new file mode 100644 index 000000000..680eac502 --- /dev/null +++ b/test/Serilog.PerformanceTests/Support/NullTextWriter.cs @@ -0,0 +1,160 @@ +using System; +using System.IO; +using System.Text; + +namespace Serilog.PerformanceTests.Support +{ + class NullTextWriter : TextWriter + { + public override void Write(char value) + { + } + + public override Encoding Encoding { get; } = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); + + public override void Write(bool value) + { + } + + public override void Write(char[] buffer) + { + } + + public override void Write(char[] buffer, int index, int count) + { + } + + public override void Write(decimal value) + { + } + + public override void Write(double value) + { + } + + public override void Write(int value) + { + } + + public override void Write(long value) + { + } + + public override void Write(object value) + { + } + + public override void Write(float value) + { + } + + public override void Write(string value) + { + } + + public override void Write(string format, object arg0) + { + } + + public override void Write(string format, object arg0, object arg1) + { + } + + public override void Write(string format, object arg0, object arg1, object arg2) + { + } + + public override void Write(string format, params object[] arg) + { + } + + public override void Write(uint value) + { + } + + public override void Write(ulong value) + { + } + + public override string ToString() + { + return String.Empty; + } + + public override void Flush() + { + } + + public override void WriteLine() + { + } + + public override void WriteLine(bool value) + { + } + + public override void WriteLine(char value) + { + } + + public override void WriteLine(char[] buffer) + { + } + + public override void WriteLine(char[] buffer, int index, int count) + { + } + + public override void WriteLine(decimal value) + { + } + + public override void WriteLine(double value) + { + } + + public override void WriteLine(int value) + { + } + + public override void WriteLine(long value) + { + } + + public override void WriteLine(object value) + { + } + + public override void WriteLine(float value) + { + } + + public override void WriteLine(string value) + { + } + + public override void WriteLine(string format, object arg0) + { + } + + public override void WriteLine(string format, object arg0, object arg1) + { + } + + public override void WriteLine(string format, object arg0, object arg1, object arg2) + { + } + + public override void WriteLine(string format, params object[] arg) + { + } + + public override void WriteLine(uint value) + { + } + + public override void WriteLine(ulong value) + { + } + } +} From c17ea150ec9587dd9ab78b65845502d603b30dbf Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Tue, 6 Jun 2017 07:46:56 +1000 Subject: [PATCH 11/12] Prevent xunit from shadow-copying test assemblies --- test/Serilog.PerformanceTests/xunit.runner.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 test/Serilog.PerformanceTests/xunit.runner.json diff --git a/test/Serilog.PerformanceTests/xunit.runner.json b/test/Serilog.PerformanceTests/xunit.runner.json new file mode 100644 index 000000000..42db7ef95 --- /dev/null +++ b/test/Serilog.PerformanceTests/xunit.runner.json @@ -0,0 +1,3 @@ +{ + "shadowCopy": false +} From e205c078b9fd0704e7bd778e2a214049472535df Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 7 Jun 2017 20:10:35 +1000 Subject: [PATCH 12/12] Fewer arg checks; seal message template token classes; encourage inlining --- src/Serilog/Events/MessageTemplate.cs | 2 ++ src/Serilog/Parsing/PropertyToken.cs | 2 +- src/Serilog/Parsing/TextToken.cs | 2 +- src/Serilog/Rendering/MessageTemplateRenderer.cs | 6 ++---- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Serilog/Events/MessageTemplate.cs b/src/Serilog/Events/MessageTemplate.cs index 240cee9ed..b1664aeea 100644 --- a/src/Serilog/Events/MessageTemplate.cs +++ b/src/Serilog/Events/MessageTemplate.cs @@ -158,6 +158,8 @@ public string Render(IReadOnlyDictionary properti /// Supplies culture-specific formatting information, or null. public void Render(IReadOnlyDictionary properties, TextWriter output, IFormatProvider formatProvider = null) { + if (properties == null) throw new ArgumentNullException(nameof(properties)); + if (output == null) throw new ArgumentNullException(nameof(output)); MessageTemplateRenderer.Render(this, properties, output, null, formatProvider); } } diff --git a/src/Serilog/Parsing/PropertyToken.cs b/src/Serilog/Parsing/PropertyToken.cs index 282f9b1e2..01dcc2109 100644 --- a/src/Serilog/Parsing/PropertyToken.cs +++ b/src/Serilog/Parsing/PropertyToken.cs @@ -25,7 +25,7 @@ namespace Serilog.Parsing /// /// A message template token representing a log event property. /// - public class PropertyToken : MessageTemplateToken + public sealed class PropertyToken : MessageTemplateToken { readonly string _rawText; readonly int? _position; diff --git a/src/Serilog/Parsing/TextToken.cs b/src/Serilog/Parsing/TextToken.cs index 974e5f530..493a1b56d 100644 --- a/src/Serilog/Parsing/TextToken.cs +++ b/src/Serilog/Parsing/TextToken.cs @@ -23,7 +23,7 @@ namespace Serilog.Parsing /// /// A message template token representing literal text. /// - public class TextToken : MessageTemplateToken + public sealed class TextToken : MessageTemplateToken { /// /// Construct a . diff --git a/src/Serilog/Rendering/MessageTemplateRenderer.cs b/src/Serilog/Rendering/MessageTemplateRenderer.cs index 7da313f09..090e39e98 100644 --- a/src/Serilog/Rendering/MessageTemplateRenderer.cs +++ b/src/Serilog/Rendering/MessageTemplateRenderer.cs @@ -15,6 +15,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Runtime.CompilerServices; using Serilog.Events; using Serilog.Formatting.Json; using Serilog.Parsing; @@ -25,12 +26,9 @@ static class MessageTemplateRenderer { static JsonValueFormatter JsonValueFormatter = new JsonValueFormatter("$type"); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Render(MessageTemplate messageTemplate, IReadOnlyDictionary properties, TextWriter output, string format = null, IFormatProvider formatProvider = null) { - if (messageTemplate == null) throw new ArgumentNullException(nameof(messageTemplate)); - if (properties == null) throw new ArgumentNullException(nameof(properties)); - if (output == null) throw new ArgumentNullException(nameof(output)); - bool isLiteral = false, isJson = false; if (format != null)