From 68bd319531d8cab568beb4812bbe25b41297bea9 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Tue, 26 Jul 2016 14:39:11 +1000 Subject: [PATCH 01/13] Dev version bump [Skip CI] --- src/Serilog/project.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog/project.json b/src/Serilog/project.json index 6ec34a238..c7bede7d0 100644 --- a/src/Serilog/project.json +++ b/src/Serilog/project.json @@ -1,5 +1,5 @@ { - "version": "2.1.0-*", + "version": "2.1.1-*", "description": "Simple .NET logging with fully-structured events", "authors": [ "Serilog Contributors" ], From f82e3d585354f29848e3425bab9a7a480dae0d74 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 28 Jul 2016 12:14:10 +1000 Subject: [PATCH 02/13] Deprecate virtual extension points of JsonFormatter in preparation for cleaning up this class --- src/Serilog/Formatting/Json/JsonFormatter.cs | 43 +++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/Serilog/Formatting/Json/JsonFormatter.cs b/src/Serilog/Formatting/Json/JsonFormatter.cs index db094a101..ea2f8e768 100644 --- a/src/Serilog/Formatting/Json/JsonFormatter.cs +++ b/src/Serilog/Formatting/Json/JsonFormatter.cs @@ -30,12 +30,35 @@ namespace Serilog.Formatting.Json /// public class JsonFormatter : ITextFormatter { + const string ExtensionPointObsoletionMessage = "Extension of JsonFormatter by subclassing is obsolete and will " + + "be removed in a future Serilog version. Write a custom formatter " + + "based on JsonValueFormatter instead. See ."; + + // Ignore obsoletion errors + #pragma warning disable 618 + readonly bool _omitEnclosingObject; readonly string _closingDelimiter; readonly bool _renderMessage; readonly IFormatProvider _formatProvider; readonly IDictionary> _literalWriters; - + + /// + /// Construct a . + /// + /// A string that will be written after each log event is formatted. + /// If null, will be used. + /// If true, the message will be rendered and written to the output as a + /// property named RenderedMessage. + /// Supplies culture-specific formatting information, or null. + public JsonFormatter( + string closingDelimiter = null, + bool renderMessage = false, + IFormatProvider formatProvider = null) + :this(false, closingDelimiter, renderMessage, formatProvider) + { + } + /// /// Construct a . /// @@ -48,8 +71,9 @@ public class JsonFormatter : ITextFormatter /// If true, the message will be rendered and written to the output as a /// property named RenderedMessage. /// Supplies culture-specific formatting information, or null. + [Obsolete("The omitEnclosingObject parameter is obsolete and will be removed in a future Serilog version.")] public JsonFormatter( - bool omitEnclosingObject = false, + bool omitEnclosingObject, string closingDelimiter = null, bool renderMessage = false, IFormatProvider formatProvider = null) @@ -136,6 +160,7 @@ public void Format(LogEvent logEvent, TextWriter output) /// /// The type of values, which handles. /// The function, which writes the values. + [Obsolete(ExtensionPointObsoletionMessage)] protected void AddLiteralWriter(Type type, Action writer) { if (type == null) throw new ArgumentNullException(nameof(type)); @@ -147,6 +172,7 @@ protected void AddLiteralWriter(Type type, Action writer) /// /// Writes out individual renderings of attached properties /// + [Obsolete(ExtensionPointObsoletionMessage)] protected virtual void WriteRenderings(IGrouping[] tokensWithFormat, IReadOnlyDictionary properties, TextWriter output) { output.Write(",\"{0}\":{{", "Renderings"); @@ -157,6 +183,7 @@ protected virtual void WriteRenderings(IGrouping[] tokens /// /// Writes out the values of individual renderings of attached properties /// + [Obsolete(ExtensionPointObsoletionMessage)] protected virtual void WriteRenderingsValues(IGrouping[] tokensWithFormat, IReadOnlyDictionary properties, TextWriter output) { var rdelim = ""; @@ -193,6 +220,7 @@ protected virtual void WriteRenderingsValues(IGrouping[] /// /// Writes out the attached properties /// + [Obsolete(ExtensionPointObsoletionMessage)] protected virtual void WriteProperties(IReadOnlyDictionary properties, TextWriter output) { output.Write(",\"{0}\":{{", "Properties"); @@ -203,6 +231,7 @@ protected virtual void WriteProperties(IReadOnlyDictionary /// Writes out the attached properties values /// + [Obsolete(ExtensionPointObsoletionMessage)] protected virtual void WritePropertiesValues(IReadOnlyDictionary properties, TextWriter output) { var precedingDelimiter = ""; @@ -215,6 +244,7 @@ protected virtual void WritePropertiesValues(IReadOnlyDictionary /// Writes out the attached exception /// + [Obsolete(ExtensionPointObsoletionMessage)] protected virtual void WriteException(Exception exception, ref string delim, TextWriter output) { WriteJsonProperty("Exception", exception, ref delim, output); @@ -223,6 +253,7 @@ protected virtual void WriteException(Exception exception, ref string delim, Tex /// /// (Optionally) writes out the rendered message /// + [Obsolete(ExtensionPointObsoletionMessage)] protected virtual void WriteRenderedMessage(string message, ref string delim, TextWriter output) { WriteJsonProperty("RenderedMessage", message, ref delim, output); @@ -231,6 +262,7 @@ protected virtual void WriteRenderedMessage(string message, ref string delim, Te /// /// Writes out the message template for the logevent. /// + [Obsolete(ExtensionPointObsoletionMessage)] protected virtual void WriteMessageTemplate(string template, ref string delim, TextWriter output) { WriteJsonProperty("MessageTemplate", template, ref delim, output); @@ -239,6 +271,7 @@ protected virtual void WriteMessageTemplate(string template, ref string delim, T /// /// Writes out the log level /// + [Obsolete(ExtensionPointObsoletionMessage)] protected virtual void WriteLevel(LogEventLevel level, ref string delim, TextWriter output) { WriteJsonProperty("Level", level, ref delim, output); @@ -247,6 +280,7 @@ protected virtual void WriteLevel(LogEventLevel level, ref string delim, TextWri /// /// Writes out the log timestamp /// + [Obsolete(ExtensionPointObsoletionMessage)] protected virtual void WriteTimestamp(DateTimeOffset timestamp, ref string delim, TextWriter output) { WriteJsonProperty("Timestamp", timestamp, ref delim, output); @@ -255,6 +289,7 @@ protected virtual void WriteTimestamp(DateTimeOffset timestamp, ref string delim /// /// Writes out a structure property /// + [Obsolete(ExtensionPointObsoletionMessage)] protected virtual void WriteStructure(string typeTag, IEnumerable properties, TextWriter output) { output.Write("{"); @@ -272,6 +307,7 @@ protected virtual void WriteStructure(string typeTag, IEnumerable /// Writes out a sequence property /// + [Obsolete(ExtensionPointObsoletionMessage)] protected virtual void WriteSequence(IEnumerable elements, TextWriter output) { output.Write("["); @@ -288,6 +324,7 @@ protected virtual void WriteSequence(IEnumerable elements, TextWriter output) /// /// Writes out a dictionary /// + [Obsolete(ExtensionPointObsoletionMessage)] protected virtual void WriteDictionary(IReadOnlyDictionary elements, TextWriter output) { output.Write("{"); @@ -306,6 +343,7 @@ protected virtual void WriteDictionary(IReadOnlyDictionary /// Writes out a json property with the specified value on output writer /// + [Obsolete(ExtensionPointObsoletionMessage)] protected virtual void WriteJsonProperty(string name, object value, ref string precedingDelimiter, TextWriter output) { output.Write(precedingDelimiter); @@ -321,6 +359,7 @@ protected virtual void WriteJsonProperty(string name, object value, ref string p /// /// The value to be written as a json construct /// The writer to write on + [Obsolete(ExtensionPointObsoletionMessage)] protected virtual void WriteLiteralValue(object value, TextWriter output) { WriteString(value.ToString(), output); From d1b45347559bdc6ccee633aefeec8d152b94a71e Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 28 Jul 2016 12:19:00 +1000 Subject: [PATCH 03/13] Include PR link in deprecation comment --- src/Serilog/Formatting/Json/JsonFormatter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog/Formatting/Json/JsonFormatter.cs b/src/Serilog/Formatting/Json/JsonFormatter.cs index ea2f8e768..aded1eb29 100644 --- a/src/Serilog/Formatting/Json/JsonFormatter.cs +++ b/src/Serilog/Formatting/Json/JsonFormatter.cs @@ -32,7 +32,7 @@ public class JsonFormatter : ITextFormatter { const string ExtensionPointObsoletionMessage = "Extension of JsonFormatter by subclassing is obsolete and will " + "be removed in a future Serilog version. Write a custom formatter " + - "based on JsonValueFormatter instead. See ."; + "based on JsonValueFormatter instead. See https://github.com/serilog/serilog/pull/819."; // Ignore obsoletion errors #pragma warning disable 618 From a12e4c7b41923a79ee94f189d72e9fcc09fcd1b2 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 12 Aug 2016 16:24:36 +1000 Subject: [PATCH 04/13] Support for audit logging (propagated exceptions) #820 --- .../LoggerAuditSinkConfiguration.cs | 106 ++++++++++++++++++ .../Configuration/LoggerSinkConfiguration.cs | 7 +- src/Serilog/Core/Sinks/AggregateSink.cs | 54 +++++++++ src/Serilog/Core/Sinks/FilteringSink.cs | 9 +- src/Serilog/Core/Sinks/SafeAggregateSink.cs | 8 +- src/Serilog/LoggerConfiguration.cs | 48 ++++++-- .../Parameters/PropertyValueConverter.cs | 15 ++- .../Core/LogEventPropertyCapturingTests.cs | 2 +- .../Core/MessageTemplateTests.cs | 2 +- .../Events/LogEventPropertyValueTests.cs | 3 +- .../Serilog.Tests/LoggerConfigurationTests.cs | 72 +++++++++++- .../Parameters/PropertyValueConverterTests.cs | 3 +- 12 files changed, 301 insertions(+), 28 deletions(-) create mode 100644 src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs create mode 100644 src/Serilog/Core/Sinks/AggregateSink.cs diff --git a/src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs b/src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs new file mode 100644 index 000000000..2a0096556 --- /dev/null +++ b/src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs @@ -0,0 +1,106 @@ +// 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 Serilog.Core; +using Serilog.Events; + +namespace Serilog.Configuration +{ + /// + /// Controls sink configuration. + /// + public class LoggerAuditSinkConfiguration + { + LoggerSinkConfiguration _sinkConfiguration; + + internal LoggerAuditSinkConfiguration(LoggerConfiguration loggerConfiguration, Action addSink, Action applyInheritedConfiguration) + { + _sinkConfiguration = new LoggerSinkConfiguration(loggerConfiguration, addSink, applyInheritedConfiguration); + } + + /// + /// Audits log events to the specified . + /// + /// The sink. + /// The minimum level for + /// events passed through the sink. Ignored when is specified. + /// A switch allowing the pass-through minimum level + /// to be changed at runtime. + /// Configuration object allowing method chaining. + public LoggerConfiguration Sink( + ILogEventSink logEventSink, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + // ReSharper disable once MethodOverloadWithOptionalParameter + LoggingLevelSwitch levelSwitch = null) + { + return _sinkConfiguration.Sink(logEventSink, restrictedToMinimumLevel, levelSwitch); + } + + /// + /// Write log events to the specified . + /// + /// The sink. + /// The minimum level for + /// events passed through the sink. Ignored when is specified. + /// A switch allowing the pass-through minimum level + /// to be changed at runtime. + /// Configuration object allowing method chaining. + public LoggerConfiguration Sink( + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) + where TSink : ILogEventSink, new() + { + return _sinkConfiguration.Sink(restrictedToMinimumLevel, levelSwitch); + } + + /// + /// Audit log events to a sub-logger, where further processing may occur. Events through + /// the sub-logger will be constrained by filters and enriched by enrichers that are + /// active in the parent. A sub-logger cannot be used to log at a more verbose level, but + /// a less verbose level is possible. + /// + /// An action that configures the sub-logger. + /// The minimum level for + /// events passed through the sink. Ignored when is specified. + /// A switch allowing the pass-through minimum level + /// to be changed at runtime. + /// Configuration object allowing method chaining. + public LoggerConfiguration Logger( + Action configureLogger, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) + { + return _sinkConfiguration.Logger(configureLogger, restrictedToMinimumLevel, levelSwitch); + } + + /// + /// Audit log events to a sub-logger, where further processing may occur. Events through + /// the sub-logger will be constrained by filters and enriched by enrichers that are + /// active in the parent. A sub-logger cannot be used to log at a more verbose level, but + /// a less verbose level is possible. + /// + /// The sub-logger. This will not be shut down automatically when the + /// parent logger is disposed. + /// The minimum level for + /// events passed through the sink. + /// Configuration object allowing method chaining. + public LoggerConfiguration Logger( + ILogger logger, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) + { + return _sinkConfiguration.Logger(logger, restrictedToMinimumLevel); + } + } +} diff --git a/src/Serilog/Configuration/LoggerSinkConfiguration.cs b/src/Serilog/Configuration/LoggerSinkConfiguration.cs index f1b7e94a4..0552387cd 100644 --- a/src/Serilog/Configuration/LoggerSinkConfiguration.cs +++ b/src/Serilog/Configuration/LoggerSinkConfiguration.cs @@ -14,7 +14,6 @@ using System; using System.ComponentModel; - using Serilog.Core; using Serilog.Core.Sinks; using Serilog.Debugging; @@ -31,8 +30,6 @@ public class LoggerSinkConfiguration readonly Action _addSink; readonly Action _applyInheritedConfiguration; - const string DefaultOutputTemplate = "{Timestamp} [{Level}] {Message}{NewLine}{Exception}"; - internal LoggerSinkConfiguration(LoggerConfiguration loggerConfiguration, Action addSink, Action applyInheritedConfiguration) { if (loggerConfiguration == null) throw new ArgumentNullException(nameof(loggerConfiguration)); @@ -51,7 +48,7 @@ internal LoggerSinkConfiguration(LoggerConfiguration loggerConfiguration, Action /// events passed through the sink. /// Configuration object allowing method chaining. /// Provided for binary compatibility for earlier versions, - /// should be removed in 2.0. Not marked obsolete because warnings + /// should be removed in 3.0. Not marked obsolete because warnings /// would be syntactically annoying to avoid. [EditorBrowsable(EditorBrowsableState.Never)] public LoggerConfiguration Sink( @@ -110,8 +107,6 @@ public LoggerConfiguration Sink( return Sink(new TSink(), restrictedToMinimumLevel, levelSwitch); } - - /// /// Write log events to a sub-logger, where further processing may occur. Events through /// the sub-logger will be constrained by filters and enriched by enrichers that are diff --git a/src/Serilog/Core/Sinks/AggregateSink.cs b/src/Serilog/Core/Sinks/AggregateSink.cs new file mode 100644 index 000000000..2fcbc24df --- /dev/null +++ b/src/Serilog/Core/Sinks/AggregateSink.cs @@ -0,0 +1,54 @@ +// Copyright 2016 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.Linq; +using Serilog.Debugging; +using Serilog.Events; + +namespace Serilog.Core.Sinks +{ + class AggregateSink : ILogEventSink + { + readonly ILogEventSink[] _sinks; + + public AggregateSink(IEnumerable sinks) + { + if (sinks == null) throw new ArgumentNullException(nameof(sinks)); + _sinks = sinks.ToArray(); + } + + public void Emit(LogEvent logEvent) + { + List exceptions = null; + foreach (var sink in _sinks) + { + try + { + sink.Emit(logEvent); + } + catch (Exception ex) + { + SelfLog.WriteLine("Caught exception while emitting to sink {0}: {1}", sink, ex); + exceptions = exceptions ?? new List(); + exceptions.Add(ex); + } + } + + if (exceptions != null) + throw new AggregateException("Failed to emit a log event", exceptions); + } + } +} diff --git a/src/Serilog/Core/Sinks/FilteringSink.cs b/src/Serilog/Core/Sinks/FilteringSink.cs index 9a5d4102b..13a898491 100644 --- a/src/Serilog/Core/Sinks/FilteringSink.cs +++ b/src/Serilog/Core/Sinks/FilteringSink.cs @@ -23,13 +23,15 @@ namespace Serilog.Core.Sinks class FilteringSink : ILogEventSink { readonly ILogEventSink _sink; + readonly bool _propagateExceptions; readonly ILogEventFilter[] _filters; - public FilteringSink(ILogEventSink sink, IEnumerable filters) + public FilteringSink(ILogEventSink sink, IEnumerable filters, bool propagateExceptions) { if (sink == null) throw new ArgumentNullException(nameof(sink)); if (filters == null) throw new ArgumentNullException(nameof(filters)); _sink = sink; + _propagateExceptions = propagateExceptions; _filters = filters.ToArray(); } @@ -47,7 +49,10 @@ public void Emit(LogEvent logEvent) } catch (Exception ex) { - SelfLog.WriteLine("Caught exception {0} while applying filters.", ex); + SelfLog.WriteLine("Caught exception while applying filters: {0}", ex); + + if (_propagateExceptions) + throw; } } } diff --git a/src/Serilog/Core/Sinks/SafeAggregateSink.cs b/src/Serilog/Core/Sinks/SafeAggregateSink.cs index 857917134..da8ad6505 100644 --- a/src/Serilog/Core/Sinks/SafeAggregateSink.cs +++ b/src/Serilog/Core/Sinks/SafeAggregateSink.cs @@ -14,6 +14,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Serilog.Debugging; using Serilog.Events; @@ -21,12 +22,12 @@ namespace Serilog.Core.Sinks { class SafeAggregateSink : ILogEventSink { - readonly IEnumerable _sinks; + readonly ILogEventSink[] _sinks; public SafeAggregateSink(IEnumerable sinks) { if (sinks == null) throw new ArgumentNullException(nameof(sinks)); - _sinks = sinks; + _sinks = sinks.ToArray(); } public void Emit(LogEvent logEvent) @@ -39,10 +40,9 @@ public void Emit(LogEvent logEvent) } catch (Exception ex) { - SelfLog.WriteLine("Caught exception {0} while emitting to sink {1}.", ex, sink); + SelfLog.WriteLine("Caught exception while emitting to sink {0}: {1}", sink, ex); } } } } } - diff --git a/src/Serilog/LoggerConfiguration.cs b/src/Serilog/LoggerConfiguration.cs index 3d8b0b646..df5dd0992 100644 --- a/src/Serilog/LoggerConfiguration.cs +++ b/src/Serilog/LoggerConfiguration.cs @@ -30,6 +30,7 @@ namespace Serilog public class LoggerConfiguration { readonly List _logEventSinks = new List(); + readonly List _auditSinks = new List(); readonly List _enrichers = new List(); readonly List _filters = new List(); readonly List _additionalScalarTypes = new List(); @@ -40,6 +41,14 @@ public class LoggerConfiguration int _maximumDestructuringDepth = 10; bool _loggerCreated; + void ApplyInheritedConfiguration(LoggerConfiguration child) + { + if (_levelSwitch != null) + child.MinimumLevel.ControlledBy(_levelSwitch); + else + child.MinimumLevel.Is(_minimumLevel); + } + /// /// Configures the sinks that log events will be emitted to. /// @@ -47,13 +56,26 @@ public LoggerSinkConfiguration WriteTo { get { - return new LoggerSinkConfiguration(this, s => _logEventSinks.Add(s), child => - { - if (_levelSwitch != null) - child.MinimumLevel.ControlledBy(_levelSwitch); - else - child.MinimumLevel.Is(_minimumLevel); - }); + return new LoggerSinkConfiguration(this, s => _logEventSinks.Add(s), ApplyInheritedConfiguration); + } + } + + /// + /// Configures sinks for auditing, instead of regular (safe) logging. When auditing is used, + /// exceptions from sinks and any intermediate filters propagate back to the caller. Most callers + /// should use instead. + /// + /// + /// Not all sinks are compatible with transactional auditing requirements (many will use asynchronous + /// batching to improve write throughput and latency). Sinks need to opt-in to auditing support by + /// extending , though the generic + /// method allows any sink class to be adapted for auditing. + /// + public LoggerAuditSinkConfiguration AuditTo + { + get + { + return new LoggerAuditSinkConfiguration(this, s => _auditSinks.Add(s), ApplyInheritedConfiguration); } } @@ -129,10 +151,18 @@ public Logger CreateLogger() ILogEventSink sink = new SafeAggregateSink(_logEventSinks); + var auditing = _auditSinks.Any(); + if (auditing) + sink = new AggregateSink(new[] { sink }.Concat(_auditSinks)); + if (_filters.Any()) - sink = new FilteringSink(sink, _filters); + { + // A throwing filter could drop an auditable event, so exceptions in filters must be propagated + // if auditing is used. + sink = new FilteringSink(sink, _filters, auditing); + } - var converter = new PropertyValueConverter(_maximumDestructuringDepth, _additionalScalarTypes, _additionalDestructuringPolicies); + var converter = new PropertyValueConverter(_maximumDestructuringDepth, _additionalScalarTypes, _additionalDestructuringPolicies, auditing); var processor = new MessageTemplateProcessor(converter); ILogEventEnricher enricher; diff --git a/src/Serilog/Parameters/PropertyValueConverter.cs b/src/Serilog/Parameters/PropertyValueConverter.cs index beeabcac0..6dae2e134 100644 --- a/src/Serilog/Parameters/PropertyValueConverter.cs +++ b/src/Serilog/Parameters/PropertyValueConverter.cs @@ -46,14 +46,20 @@ partial class PropertyValueConverter : ILogEventPropertyFactory, ILogEventProper readonly IDestructuringPolicy[] _destructuringPolicies; readonly IScalarConversionPolicy[] _scalarConversionPolicies; readonly int _maximumDestructuringDepth; + readonly bool _propagateExceptions; - public PropertyValueConverter(int maximumDestructuringDepth, IEnumerable additionalScalarTypes, IEnumerable additionalDestructuringPolicies) + public PropertyValueConverter( + int maximumDestructuringDepth, + IEnumerable additionalScalarTypes, + IEnumerable additionalDestructuringPolicies, + bool propagateExceptions) { if (additionalScalarTypes == null) throw new ArgumentNullException(nameof(additionalScalarTypes)); if (additionalDestructuringPolicies == null) throw new ArgumentNullException(nameof(additionalDestructuringPolicies)); if (maximumDestructuringDepth < 0) throw new ArgumentOutOfRangeException(nameof(maximumDestructuringDepth)); _maximumDestructuringDepth = maximumDestructuringDepth; + _propagateExceptions = propagateExceptions; _scalarConversionPolicies = new IScalarConversionPolicy[] { @@ -177,7 +183,7 @@ bool IsValidDictionaryKeyType(Type valueType) valueType.GetTypeInfo().IsEnum; } - static IEnumerable GetProperties(object value, ILogEventPropertyValueFactory recursive) + IEnumerable GetProperties(object value, ILogEventPropertyValueFactory recursive) { foreach (var prop in value.GetType().GetPropertiesRecursive()) { @@ -188,6 +194,8 @@ static IEnumerable GetProperties(object value, ILogEventProper } catch (TargetParameterCountException) { + // These properties would ideally be ignored; since they never produce values they're not + // of concern to auditing and exceptions can be suppressed. SelfLog.WriteLine("The property accessor {0} is a non-default indexer", prop); continue; } @@ -195,6 +203,9 @@ static IEnumerable GetProperties(object value, ILogEventProper { SelfLog.WriteLine("The property accessor {0} threw exception {1}", prop, ex); propValue = "The property accessor threw an exception: " + ex.InnerException.GetType().Name; + + if (_propagateExceptions) + throw; } yield return new LogEventProperty(prop.Name, recursive.CreatePropertyValue(propValue, true)); } diff --git a/test/Serilog.Tests/Core/LogEventPropertyCapturingTests.cs b/test/Serilog.Tests/Core/LogEventPropertyCapturingTests.cs index b4fb49184..b81834b43 100644 --- a/test/Serilog.Tests/Core/LogEventPropertyCapturingTests.cs +++ b/test/Serilog.Tests/Core/LogEventPropertyCapturingTests.cs @@ -179,7 +179,7 @@ static IEnumerable Capture(string messageTemplate, params obje { var mt = new MessageTemplateParser().Parse(messageTemplate); var binder = new PropertyBinder( - new PropertyValueConverter(10, Enumerable.Empty(), Enumerable.Empty())); + new PropertyValueConverter(10, Enumerable.Empty(), Enumerable.Empty(), false)); return binder.ConstructProperties(mt, properties); } diff --git a/test/Serilog.Tests/Core/MessageTemplateTests.cs b/test/Serilog.Tests/Core/MessageTemplateTests.cs index 804e4bc22..75bcee236 100644 --- a/test/Serilog.Tests/Core/MessageTemplateTests.cs +++ b/test/Serilog.Tests/Core/MessageTemplateTests.cs @@ -126,7 +126,7 @@ static string Render(string messageTemplate, params object[] properties) static string Render(IFormatProvider formatProvider, string messageTemplate, params object[] properties) { var mt = new MessageTemplateParser().Parse(messageTemplate); - var binder = new PropertyBinder(new PropertyValueConverter(10, Enumerable.Empty(), Enumerable.Empty())); + var binder = new PropertyBinder(new PropertyValueConverter(10, Enumerable.Empty(), Enumerable.Empty(), false)); var props = binder.ConstructProperties(mt, properties); var output = new StringBuilder(); var writer = new StringWriter(output); diff --git a/test/Serilog.Tests/Events/LogEventPropertyValueTests.cs b/test/Serilog.Tests/Events/LogEventPropertyValueTests.cs index 8d5ee101f..170dd2fe2 100644 --- a/test/Serilog.Tests/Events/LogEventPropertyValueTests.cs +++ b/test/Serilog.Tests/Events/LogEventPropertyValueTests.cs @@ -26,7 +26,8 @@ namespace Serilog.Tests.Events { public class LogEventPropertyValueTests { - readonly PropertyValueConverter _converter = new PropertyValueConverter(10, Enumerable.Empty(), Enumerable.Empty()); + readonly PropertyValueConverter _converter = + new PropertyValueConverter(10, Enumerable.Empty(), Enumerable.Empty(), false); [Fact] public void AnEnumIsConvertedToANonStringScalarValue() diff --git a/test/Serilog.Tests/LoggerConfigurationTests.cs b/test/Serilog.Tests/LoggerConfigurationTests.cs index a332d3c7b..eabb8386f 100644 --- a/test/Serilog.Tests/LoggerConfigurationTests.cs +++ b/test/Serilog.Tests/LoggerConfigurationTests.cs @@ -363,5 +363,75 @@ public void LowerMinimumLevelOverridesArePropagated() Assert.Equal(1, sink.Events.Count); } + + [Fact] + public void ExceptionsThrownBySinksAreNotPropagated() + { + var logger = new LoggerConfiguration() + .WriteTo.Sink(new DelegatingSink(e => { throw new Exception("Boom!"); })) + .CreateLogger(); + + logger.Write(Some.InformationEvent()); + + Assert.True(true, "No exception reached the caller"); + } + + [Fact] + public void ExceptionsThrownBySinksAreNotPropagatedEvenWhenAuditingIsPresent() + { + var logger = new LoggerConfiguration() + .AuditTo.Sink(new CollectingSink()) + .WriteTo.Sink(new DelegatingSink(e => { throw new Exception("Boom!"); })) + .CreateLogger(); + + logger.Write(Some.InformationEvent()); + + Assert.True(true, "No exception reached the caller"); + } + + [Fact] + public void ExceptionsThrownByFiltersAreNotPropagated() + { + var logger = new LoggerConfiguration() + .Filter.ByExcluding(e => { throw new Exception("Boom!"); }) + .CreateLogger(); + + logger.Write(Some.InformationEvent()); + + Assert.True(true, "No exception reached the caller"); + } + + [Fact] + public void ExceptionsThrownByAuditSinksArePropagated() + { + var logger = new LoggerConfiguration() + .AuditTo.Sink(new DelegatingSink(e => { throw new Exception("Boom!"); })) + .CreateLogger(); + + Assert.Throws(() => logger.Write(Some.InformationEvent())); + } + + [Fact] + public void ExceptionsThrownByFiltersArePropagatedIfAuditingEnabled() + { + var logger = new LoggerConfiguration() + .AuditTo.Sink(new DelegatingSink(e => { })) + .Filter.ByExcluding(e => { throw new Exception("Boom!"); }) + .CreateLogger(); + + Assert.Throws(() => logger.Write(Some.InformationEvent())); + } + + [Fact] + public void ExceptionsThrownByAuditSinksArePropagatedFromChildLoggers() + { + var logger = new LoggerConfiguration() + .AuditTo.Sink(new DelegatingSink(e => { throw new Exception("Boom!"); })) + .CreateLogger(); + + Assert.Throws(() => logger + .ForContext() + .Write(Some.InformationEvent())); + } } -} \ No newline at end of file +} diff --git a/test/Serilog.Tests/Parameters/PropertyValueConverterTests.cs b/test/Serilog.Tests/Parameters/PropertyValueConverterTests.cs index 438011981..354202eab 100644 --- a/test/Serilog.Tests/Parameters/PropertyValueConverterTests.cs +++ b/test/Serilog.Tests/Parameters/PropertyValueConverterTests.cs @@ -12,7 +12,8 @@ namespace Serilog.Tests.Parameters { public class PropertyValueConverterTests { - readonly PropertyValueConverter _converter = new PropertyValueConverter(10, Enumerable.Empty(), Enumerable.Empty()); + readonly PropertyValueConverter _converter = + new PropertyValueConverter(10, Enumerable.Empty(), Enumerable.Empty(), false); [Fact] public void UnderDestructuringAByteArrayIsAScalarValue() From f0ceed51a3e9cec1ac8ded0281bb2bccadb66ed8 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 12 Aug 2016 16:27:23 +1000 Subject: [PATCH 05/13] Exception message punctuation --- src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs | 2 +- src/Serilog/Core/Sinks/AggregateSink.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs b/src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs index 2a0096556..6054bf6a3 100644 --- a/src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs +++ b/src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs @@ -1,4 +1,4 @@ -// Copyright 2013-2015 Serilog Contributors +// Copyright 2016 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/src/Serilog/Core/Sinks/AggregateSink.cs b/src/Serilog/Core/Sinks/AggregateSink.cs index 2fcbc24df..c43cd3889 100644 --- a/src/Serilog/Core/Sinks/AggregateSink.cs +++ b/src/Serilog/Core/Sinks/AggregateSink.cs @@ -48,7 +48,7 @@ public void Emit(LogEvent logEvent) } if (exceptions != null) - throw new AggregateException("Failed to emit a log event", exceptions); + throw new AggregateException("Failed to emit a log event.", exceptions); } } } From d8cee48b086fa4f3c8c1e1e8c8ca230c5b0c3c16 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 12 Aug 2016 16:28:35 +1000 Subject: [PATCH 06/13] Consistent XML doc messages --- src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs b/src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs index 6054bf6a3..9381f7121 100644 --- a/src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs +++ b/src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs @@ -31,7 +31,7 @@ internal LoggerAuditSinkConfiguration(LoggerConfiguration loggerConfiguration, A } /// - /// Audits log events to the specified . + /// Audit log events to the specified . /// /// The sink. /// The minimum level for @@ -49,7 +49,7 @@ public LoggerConfiguration Sink( } /// - /// Write log events to the specified . + /// Audit log events to the specified . /// /// The sink. /// The minimum level for From 59259baa71bf3668ad1940e593ad1582c4b0f2a3 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 12 Aug 2016 17:29:52 +1000 Subject: [PATCH 07/13] Additional exception behavior tests and fixes --- src/Serilog/LoggerConfiguration.cs | 16 +----- .../Parameters/PropertyValueConverter.cs | 21 +++++-- .../Serilog.Tests/LoggerConfigurationTests.cs | 57 +++++++++++++++++++ 3 files changed, 76 insertions(+), 18 deletions(-) diff --git a/src/Serilog/LoggerConfiguration.cs b/src/Serilog/LoggerConfiguration.cs index df5dd0992..764d937e2 100644 --- a/src/Serilog/LoggerConfiguration.cs +++ b/src/Serilog/LoggerConfiguration.cs @@ -52,13 +52,7 @@ void ApplyInheritedConfiguration(LoggerConfiguration child) /// /// Configures the sinks that log events will be emitted to. /// - public LoggerSinkConfiguration WriteTo - { - get - { - return new LoggerSinkConfiguration(this, s => _logEventSinks.Add(s), ApplyInheritedConfiguration); - } - } + public LoggerSinkConfiguration WriteTo => new LoggerSinkConfiguration(this, s => _logEventSinks.Add(s), ApplyInheritedConfiguration); /// /// Configures sinks for auditing, instead of regular (safe) logging. When auditing is used, @@ -71,13 +65,7 @@ public LoggerSinkConfiguration WriteTo /// extending , though the generic /// method allows any sink class to be adapted for auditing. /// - public LoggerAuditSinkConfiguration AuditTo - { - get - { - return new LoggerAuditSinkConfiguration(this, s => _auditSinks.Add(s), ApplyInheritedConfiguration); - } - } + public LoggerAuditSinkConfiguration AuditTo => new LoggerAuditSinkConfiguration(this, s => _auditSinks.Add(s), ApplyInheritedConfiguration); /// /// Configures the minimum level at which events will be passed to sinks. If diff --git a/src/Serilog/Parameters/PropertyValueConverter.cs b/src/Serilog/Parameters/PropertyValueConverter.cs index 6dae2e134..49bf9a825 100644 --- a/src/Serilog/Parameters/PropertyValueConverter.cs +++ b/src/Serilog/Parameters/PropertyValueConverter.cs @@ -90,7 +90,19 @@ public LogEventPropertyValue CreatePropertyValue(object value, bool destructureO public LogEventPropertyValue CreatePropertyValue(object value, Destructuring destructuring) { - return CreatePropertyValue(value, destructuring, 1); + try + { + return CreatePropertyValue(value, destructuring, 1); + } + catch (Exception ex) + { + SelfLog.WriteLine("Exception caught while converting property value: {0}", ex); + + if (_propagateExceptions) + throw; + + return new ScalarValue("Capturing the property value threw an exception: " + ex.GetType().Name); + } } LogEventPropertyValue CreatePropertyValue(object value, bool destructureObjects, int depth) @@ -115,7 +127,7 @@ LogEventPropertyValue CreatePropertyValue(object value, Destructuring destructur var limiter = new DepthLimiter(depth, _maximumDestructuringDepth, this); foreach (var scalarConversionPolicy in _scalarConversionPolicies) - { + { ScalarValue converted; if (scalarConversionPolicy.TryConvertToScalar(value, limiter, out converted)) return converted; @@ -201,11 +213,12 @@ IEnumerable GetProperties(object value, ILogEventPropertyValue } catch (TargetInvocationException ex) { - SelfLog.WriteLine("The property accessor {0} threw exception {1}", prop, ex); - propValue = "The property accessor threw an exception: " + ex.InnerException.GetType().Name; + SelfLog.WriteLine("The property accessor {0} threw exception: {1}", prop, ex); if (_propagateExceptions) throw; + + propValue = "The property accessor threw an exception: " + ex.InnerException.GetType().Name; } yield return new LogEventProperty(prop.Name, recursive.CreatePropertyValue(propValue, true)); } diff --git a/test/Serilog.Tests/LoggerConfigurationTests.cs b/test/Serilog.Tests/LoggerConfigurationTests.cs index eabb8386f..a755bda26 100644 --- a/test/Serilog.Tests/LoggerConfigurationTests.cs +++ b/test/Serilog.Tests/LoggerConfigurationTests.cs @@ -433,5 +433,62 @@ public void ExceptionsThrownByAuditSinksArePropagatedFromChildLoggers() .ForContext() .Write(Some.InformationEvent())); } + + class Value { } + + [Fact] + public void ExceptionsThrownByDestructuringPoliciesAreNotPropagated() + { + var logger = new LoggerConfiguration() + .WriteTo.Sink(new CollectingSink()) + .Destructure.ByTransforming(v => { throw new Exception("Boom!"); }) + .CreateLogger(); + + logger.Information("{@Value}", new Value()); + + Assert.True(true, "No exception reached the caller"); + } + + [Fact] + public void ExceptionsThrownByDestructuringPoliciesArePropagatedIfAuditingEnabled() + { + var logger = new LoggerConfiguration() + .AuditTo.Sink(new CollectingSink()) + .Destructure.ByTransforming(v => { throw new Exception("Boom!"); }) + .CreateLogger(); + + Assert.Throws(() => logger.Information("{@Value}", new Value())); + } + + class ThrowingProperty + { + // ReSharper disable once UnusedMember.Local + public string Property + { + get { throw new Exception("Boom!"); } + } + } + + [Fact] + public void ExceptionsThrownByPropertyAccessorsAreNotPropagated() + { + var logger = new LoggerConfiguration() + .WriteTo.Sink(new CollectingSink()) + .CreateLogger(); + + logger.Information("{@Value}", new ThrowingProperty()); + + Assert.True(true, "No exception reached the caller"); + } + + [Fact] + public void ExceptionsThrownByPropertyAccessorsArePropagatedIfAuditingEnabled() + { + var logger = new LoggerConfiguration() + .AuditTo.Sink(new CollectingSink()) + .CreateLogger(); + + Assert.Throws(() => logger.Information("{@Value}", new ThrowingProperty())); + } } } From d8cd24777a04dd0af9715e5a3b81aa2dde0d3131 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 12 Aug 2016 19:03:42 +1000 Subject: [PATCH 08/13] Having a shot at @joshka's suggested workaround for macOS build failures --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 16ba7d9dc..8f2a25c41 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,6 +41,7 @@ before_install: - sudo bash ./scripts/obtain/install.sh --channel "preview" --version "$CLI_VERSION" --install-dir "$DOTNET_INSTALL_DIR" --no-path # add dotnet to PATH - export PATH="$DOTNET_INSTALL_DIR:$PATH" + - if test "$TRAVIS_OS_NAME" == "osx"; then sudo install_name_tool -add_rpath /usr/local/opt/openssl/lib /usr/local/share/dotnet/shared/Microsoft.NETCore.App/1.0.0/System.Security.Cryptography.Native.dylib; fi script: - ./build.sh \ No newline at end of file From 9c99a9302d427a99de4ee12905595979efd64a44 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 12 Aug 2016 19:25:05 +1000 Subject: [PATCH 09/13] Disable the osx target in .travis.yml --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8f2a25c41..abdf1132a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,6 @@ addons: - zlib1g os: - - osx - linux env: @@ -41,7 +40,6 @@ before_install: - sudo bash ./scripts/obtain/install.sh --channel "preview" --version "$CLI_VERSION" --install-dir "$DOTNET_INSTALL_DIR" --no-path # add dotnet to PATH - export PATH="$DOTNET_INSTALL_DIR:$PATH" - - if test "$TRAVIS_OS_NAME" == "osx"; then sudo install_name_tool -add_rpath /usr/local/opt/openssl/lib /usr/local/share/dotnet/shared/Microsoft.NETCore.App/1.0.0/System.Security.Cryptography.Native.dylib; fi script: - ./build.sh \ No newline at end of file From 3089b5f16d514be931b9df9c742dbde93c11e627 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Tue, 16 Aug 2016 07:36:54 +1000 Subject: [PATCH 10/13] Bump dev version to 2.2 (features added) --- src/Serilog/project.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog/project.json b/src/Serilog/project.json index c7bede7d0..692b5faf5 100644 --- a/src/Serilog/project.json +++ b/src/Serilog/project.json @@ -1,5 +1,5 @@ { - "version": "2.1.1-*", + "version": "2.2.0-*", "description": "Simple .NET logging with fully-structured events", "authors": [ "Serilog Contributors" ], From bc445014fae8d4a93db5800a80a32bc00ea86859 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 19 Aug 2016 17:11:13 +1000 Subject: [PATCH 11/13] Updated (micro) benchmarks --- RunPerfTests.ps1 | 21 +-- Serilog.sln | 5 +- .../ForContextTests.cs | 80 --------- .../FromLogContextPushPropertyTests.cs | 99 ------------ test/Serilog.PerformanceTests/Harness.cs | 39 ++--- .../LevelControlBenchmark.cs | 66 ++++++++ .../LogContextEnrichmentBenchmark.cs | 63 ++++++++ .../Serilog.PerformanceTests/LogEventTests.cs | 72 --------- .../MessageTemplateParsingBenchmark.cs | 41 +++++ .../MinimumLevelTests.cs | 152 ------------------ .../NestedLoggerCreationBenchmark.cs | 33 ++++ .../NestedLoggerLatencyBenchmark.cs | 42 +++++ ...ateParserTests.cs => PipelineBenchmark.cs} | 33 ++-- .../Support/NullSink.cs | 12 ++ test/Serilog.PerformanceTests/Support/Some.cs | 20 +++ test/Serilog.PerformanceTests/project.json | 4 +- 16 files changed, 325 insertions(+), 457 deletions(-) delete mode 100644 test/Serilog.PerformanceTests/ForContextTests.cs delete mode 100644 test/Serilog.PerformanceTests/FromLogContextPushPropertyTests.cs create mode 100644 test/Serilog.PerformanceTests/LevelControlBenchmark.cs create mode 100644 test/Serilog.PerformanceTests/LogContextEnrichmentBenchmark.cs delete mode 100644 test/Serilog.PerformanceTests/LogEventTests.cs create mode 100644 test/Serilog.PerformanceTests/MessageTemplateParsingBenchmark.cs delete mode 100644 test/Serilog.PerformanceTests/MinimumLevelTests.cs create mode 100644 test/Serilog.PerformanceTests/NestedLoggerCreationBenchmark.cs create mode 100644 test/Serilog.PerformanceTests/NestedLoggerLatencyBenchmark.cs rename test/Serilog.PerformanceTests/{MessageTemplateParserTests.cs => PipelineBenchmark.cs} (60%) create mode 100644 test/Serilog.PerformanceTests/Support/NullSink.cs create mode 100644 test/Serilog.PerformanceTests/Support/Some.cs diff --git a/RunPerfTests.ps1 b/RunPerfTests.ps1 index 8e83073ce..bc6cda3df 100644 --- a/RunPerfTests.ps1 +++ b/RunPerfTests.ps1 @@ -1,21 +1,16 @@ Push-Location $PSScriptRoot -if(Test-Path .\artifacts) { Remove-Item .\artifacts -Force -Recurse } +./Build.ps1 -& dotnet restore +foreach ($test in ls test/*.PerformanceTests) { + Push-Location $test -$revision = @{ $true = $env:APPVEYOR_BUILD_NUMBER; $false = 1 }[$env:APPVEYOR_BUILD_NUMBER -ne $NULL]; + echo "perf: Running performance test project in $test" -Push-Location src/Serilog + & dotnet test -c Release + if($LASTEXITCODE -ne 0) { exit 2 } -& dotnet build -c Release -o ..\..\.\artifacts --version-suffix=$revision -if($LASTEXITCODE -ne 0) { exit 1 } - -Pop-Location -Push-Location test/Serilog.PerformanceTests - -& dotnet test -c Release -if($LASTEXITCODE -ne 0) { exit 2 } + Pop-Location +} Pop-Location -Pop-Location diff --git a/Serilog.sln b/Serilog.sln index d8a0c1773..32d2b0d9b 100644 --- a/Serilog.sln +++ b/Serilog.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{037440DE-440B-4129-9F7A-09B42D00397E}" EndProject @@ -12,10 +12,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{E9D1B5 .editorconfig = .editorconfig appveyor.yml = appveyor.yml Build.ps1 = Build.ps1 + build.sh = build.sh CHANGES.md = CHANGES.md global.json = global.json NuGet.config = NuGet.config README.md = README.md + run_perf_tests.sh = run_perf_tests.sh + RunPerfTests.ps1 = RunPerfTests.ps1 assets\Serilog.snk = assets\Serilog.snk EndProjectSection EndProject diff --git a/test/Serilog.PerformanceTests/ForContextTests.cs b/test/Serilog.PerformanceTests/ForContextTests.cs deleted file mode 100644 index cb07318c5..000000000 --- a/test/Serilog.PerformanceTests/ForContextTests.cs +++ /dev/null @@ -1,80 +0,0 @@ - -// Copyright 2013-2016 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 BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; -using Serilog.Tests.Support; -using Serilog; -using System; -using Xunit; - -namespace Serilog.PerformanceTests -{ - /// - /// From https://github.com/serilog/serilog/pull/750 - /// - public class ForContextTests - { - ILogger log; - - [Setup] - public void Setup() - { - log = new LoggerConfiguration() - .WriteTo.Sink(new DelegatingSink(e => { })) - .CreateLogger(); - } - - [Benchmark(Baseline = true)] - public void Baseline() - { - for (var i = 0; i < 10; ++i) - { - log.Information("Event!"); - } - } - - [Benchmark] - public void ForContextInfo10() - { - for (var i = 0; i < 10; ++i) - { - var ctx = log.ForContext("I", i); - ctx.Information("Event!"); - } - } - - [Benchmark] - public void ForContextInfo1000() - { - for (var i = 0; i < 1000; ++i) - { - var ctx = log.ForContext("I", i); - ctx.Information("Event!"); - } - } - - [Benchmark] - public void ForContextInfo10000() - { - for (var i = 0; i < 10000; ++i) - { - var ctx = log.ForContext("I", i); - ctx.Information("Event!"); - } - } - } -} - \ No newline at end of file diff --git a/test/Serilog.PerformanceTests/FromLogContextPushPropertyTests.cs b/test/Serilog.PerformanceTests/FromLogContextPushPropertyTests.cs deleted file mode 100644 index 8008829e4..000000000 --- a/test/Serilog.PerformanceTests/FromLogContextPushPropertyTests.cs +++ /dev/null @@ -1,99 +0,0 @@ - -// Copyright 2013-2016 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 BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; -using Serilog.Tests.Support; -using Serilog; -using Serilog.Context; -using System; -using Xunit; - -namespace Serilog.PerformanceTests -{ - public class FromLogContextPushPropertyTests - { - ILogger log; - - [Setup] - public void Setup() - { - log = new LoggerConfiguration() - .WriteTo.Sink(new DelegatingSink(e => { })) - .Enrich.FromLogContext() - .CreateLogger(); - } - - [Benchmark(Baseline = true)] - public void Baseline() - { - for (var i = 0; i < 1000; ++i) - { - log.Information("Event!"); - } - } - - [Benchmark] - public void Push1Property1000() - { - for (var i = 0; i < 1000; ++i) - { - using (LogContext.PushProperty("A", 2)) - { - log.Information("Event!"); - } - } - } - - [Benchmark] - public void Push1Property10000() - { - for (var i = 0; i < 10000; ++i) - { - using (LogContext.PushProperty("A", 2)) - { - log.Information("Event!"); - } - } - } - - [Benchmark] - public void Push2Properties1000() - { - for (var i = 0; i < 1000; ++i) - { - using (LogContext.PushProperty("A", 2)) - using (LogContext.PushProperty("B", 1)) - { - log.Information("Event!"); - } - } - } - - [Benchmark] - public void Push2Properties10000() - { - for (var i = 0; i < 10000; ++i) - { - using (LogContext.PushProperty("A", 2)) - using (LogContext.PushProperty("B", 1)) - { - log.Information("Event!"); - } - } - } - } -} - \ No newline at end of file diff --git a/test/Serilog.PerformanceTests/Harness.cs b/test/Serilog.PerformanceTests/Harness.cs index 6ba154bba..2ab3e109c 100644 --- a/test/Serilog.PerformanceTests/Harness.cs +++ b/test/Serilog.PerformanceTests/Harness.cs @@ -15,7 +15,6 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; -using Serilog.Tests.Support; using Serilog; using Serilog.Events; using System; @@ -23,46 +22,42 @@ namespace Serilog.PerformanceTests { - // TODO: - - // For Context - // MinimumLevel - // Push - ForContext - // Ctor of LogEvent - // Message Template parsing - - // property binding perf (Bind message template) - - public class Runner + public class Harness { [Fact] - public void ForContext() + public void LogContextEnrichment() { - var context = BenchmarkRunner.Run(); + BenchmarkRunner.Run(); } [Fact] - public void MinimumLevel() + public void MessageTemplateParsing() { - var context = BenchmarkRunner.Run(); + BenchmarkRunner.Run(); } [Fact] - public void FromLogContextPushProperty() + public void LevelControl() + { + BenchmarkRunner.Run(); + } + + [Fact] + public void NestedLoggerCreation() { - var context = BenchmarkRunner.Run(); + BenchmarkRunner.Run(); } [Fact] - public void LogEvent() + public void NestedLoggerLatency() { - var context = BenchmarkRunner.Run(); + BenchmarkRunner.Run(); } [Fact] - public void MessageTemplateParser() + public void Pipeline() { - var context = BenchmarkRunner.Run(); + BenchmarkRunner.Run(); } } } \ No newline at end of file diff --git a/test/Serilog.PerformanceTests/LevelControlBenchmark.cs b/test/Serilog.PerformanceTests/LevelControlBenchmark.cs new file mode 100644 index 000000000..899212357 --- /dev/null +++ b/test/Serilog.PerformanceTests/LevelControlBenchmark.cs @@ -0,0 +1,66 @@ +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 +{ + /// + /// Tests the overhead of determining the active logging level. + /// + public class LevelControlBenchmark + { + ILogger _off, _levelSwitchOff, _minLevel, _levelSwitch; + readonly LogEvent _event = Some.InformationEvent(); + + [Setup] + public void Setup() + { + _off = new LoggerConfiguration() + .MinimumLevel.Fatal() + .WriteTo.Sink(new NullSink()) + .CreateLogger(); + _levelSwitchOff = new LoggerConfiguration() + .MinimumLevel.ControlledBy(new LoggingLevelSwitch(LogEventLevel.Fatal)) + .WriteTo.Sink(new NullSink()) + .CreateLogger(); + _minLevel = new LoggerConfiguration() + .MinimumLevel.Information() + .WriteTo.Sink(new NullSink()) + .CreateLogger(); + _levelSwitch = new LoggerConfiguration() + .MinimumLevel.ControlledBy(new LoggingLevelSwitch(LogEventLevel.Information)) + .WriteTo.Sink(new NullSink()) + .CreateLogger(); + } + + [Benchmark(Baseline = true)] + public void Off() + { + _off.Write(_event); + } + + [Benchmark] + public void LevelSwitchOff() + { + _levelSwitchOff.Write(_event); + } + + [Benchmark] + public void MinimumLevelOn() + { + _minLevel.Write(_event); + } + + [Benchmark] + public void LevelSwitchOn() + { + _levelSwitch.Write(_event); + } + } +} + \ No newline at end of file diff --git a/test/Serilog.PerformanceTests/LogContextEnrichmentBenchmark.cs b/test/Serilog.PerformanceTests/LogContextEnrichmentBenchmark.cs new file mode 100644 index 000000000..c353fccfb --- /dev/null +++ b/test/Serilog.PerformanceTests/LogContextEnrichmentBenchmark.cs @@ -0,0 +1,63 @@ +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 +{ + public class LogContextEnrichmentBenchmark + { + ILogger _bare, _enriched; + readonly LogEvent _event = Some.InformationEvent(); + + [Setup] + public void Setup() + { + _bare = new LoggerConfiguration() + .WriteTo.Sink(new NullSink()) + .CreateLogger(); + + _enriched = new LoggerConfiguration() + .WriteTo.Sink(new NullSink()) + .Enrich.FromLogContext() + .CreateLogger(); + } + + [Benchmark(Baseline = true)] + public void Bare() + { + _bare.Write(_event); + } + + [Benchmark] + public void PushProperty() + { + using (LogContext.PushProperty("A", "B")) + { + } + } + + [Benchmark] + public void PushPropertyNested() + { + using (LogContext.PushProperty("A", "B")) + using (LogContext.PushProperty("C", "D")) + { + } + } + + [Benchmark] + public void PushPropertyEnriched() + { + using (LogContext.PushProperty("A", "B")) + { + _enriched.Write(_event); + } + } + } +} + \ No newline at end of file diff --git a/test/Serilog.PerformanceTests/LogEventTests.cs b/test/Serilog.PerformanceTests/LogEventTests.cs deleted file mode 100644 index 7f62ff972..000000000 --- a/test/Serilog.PerformanceTests/LogEventTests.cs +++ /dev/null @@ -1,72 +0,0 @@ - -// Copyright 2013-2016 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 BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; -using Serilog.Tests.Support; -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 -{ - public class LogEventTests - { - List _properties; - Exception _exception; - MessageTemplate _emptyMessageTemplate; - - [Setup] - public void Setup() - { - _exception = new Exception("An Error"); - _emptyMessageTemplate = new MessageTemplate(Enumerable.Empty()); - _properties = new List(); - - var items = Enumerable.Range(0,1000); - foreach (var item in items) - { - var prop = new LogEventProperty(item.ToString(), new ScalarValue(item)); - _properties.Add(prop); - } - } - - [Benchmark(Baseline = true)] - public void Baseline() - { - var le = new LogEvent(DateTimeOffset.Now, - LogEventLevel.Information, - _exception, - _emptyMessageTemplate, - Enumerable.Empty()); - } - - [Benchmark()] - public void LogEvent1000Properties() - { - var le = new LogEvent(DateTimeOffset.Now, - LogEventLevel.Information, - _exception, - _emptyMessageTemplate, - _properties); - } - } -} - \ No newline at end of file diff --git a/test/Serilog.PerformanceTests/MessageTemplateParsingBenchmark.cs b/test/Serilog.PerformanceTests/MessageTemplateParsingBenchmark.cs new file mode 100644 index 000000000..d9c3fcfe3 --- /dev/null +++ b/test/Serilog.PerformanceTests/MessageTemplateParsingBenchmark.cs @@ -0,0 +1,41 @@ +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 +{ + /// + /// Tests the cost of parsing various message templates. + /// + public class MessageTemplateParsingBenchmark + { + MessageTemplateParser _parser; + const string _DefaultConsoleOutputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}"; + + [Setup] + public void Setup() + { + _parser = new MessageTemplateParser(); + } + + [Benchmark(Baseline = true)] + public void EmptyTemplate() + { + _parser.Parse(""); + } + + [Benchmark] + public void DefaultConsoleOutputTemplate() + { + _parser.Parse(_DefaultConsoleOutputTemplate); + } + } +} + \ No newline at end of file diff --git a/test/Serilog.PerformanceTests/MinimumLevelTests.cs b/test/Serilog.PerformanceTests/MinimumLevelTests.cs deleted file mode 100644 index 6158f2f5c..000000000 --- a/test/Serilog.PerformanceTests/MinimumLevelTests.cs +++ /dev/null @@ -1,152 +0,0 @@ - -// Copyright 2013-2016 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 BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; -using Serilog; -using Serilog.Core; -using Serilog.Events; -using Serilog.Tests.Support; -using System; -using Xunit; - -namespace Serilog.PerformanceTests -{ - public class MinimumLevelTests - { - ILogger log; - LoggingLevelSwitch levelSwitch; - - [Setup] - public void Setup() - { - levelSwitch = new LoggingLevelSwitch(); - levelSwitch.MinimumLevel = LogEventLevel.Verbose; - - log = new LoggerConfiguration() - .MinimumLevel.ControlledBy(levelSwitch) - .WriteTo.Sink(new DelegatingSink(e => { })) - .CreateLogger(); - } - - [Benchmark(Baseline = true)] - public void Baseline() - { - //By default this is info, set to verbose for baseline - for (var i = 0; i < 1000; ++i) - { - log.Verbose("A verbose event!"); - log.Debug("A debug event!"); - log.Information("An info event!"); - log.Warning("A warm event!"); - log.Error("An error event!"); - log.Fatal("A fatal event!"); - } - } - - [Benchmark] - public void MinimumVerbose() - { - levelSwitch.MinimumLevel = LogEventLevel.Verbose; - - for (var i = 0; i < 1000; ++i) - { - log.Verbose("A verbose event!"); - log.Debug("A debug event!"); - log.Information("An info event!"); - log.Warning("A warm event!"); - log.Error("An error event!"); - log.Fatal("A fatal event!"); - } - } - - [Benchmark] - public void MinimumDebug() - { - levelSwitch.MinimumLevel = LogEventLevel.Debug; - - for (var i = 0; i < 1000; ++i) - { - log.Verbose("A verbose event!"); - log.Debug("A debug event!"); - log.Information("An info event!"); - log.Warning("A warm event!"); - log.Error("An error event!"); - log.Fatal("A fatal event!"); - } - } - - [Benchmark] - public void MinimumInfo() - { - levelSwitch.MinimumLevel = LogEventLevel.Information; - for (var i = 0; i < 1000; ++i) - { - log.Verbose("A verbose event!"); - log.Debug("A debug event!"); - log.Information("An info event!"); - log.Warning("A warm event!"); - log.Error("An error event!"); - log.Fatal("A fatal event!"); - } - } - - [Benchmark] - public void MinimumWarn() - { - levelSwitch.MinimumLevel = LogEventLevel.Warning; - for (var i = 0; i < 1000; ++i) - { - log.Verbose("A verbose event!"); - log.Debug("A debug event!"); - log.Information("An info event!"); - log.Warning("A warm event!"); - log.Error("An error event!"); - log.Fatal("A fatal event!"); - } - } - - [Benchmark] - public void MinimumError() - { - levelSwitch.MinimumLevel = LogEventLevel.Error; - for (var i = 0; i < 1000; ++i) - { - log.Verbose("A verbose event!"); - log.Debug("A debug event!"); - log.Information("An info event!"); - log.Warning("A warm event!"); - log.Error("An error event!"); - log.Fatal("A fatal event!"); - } - } - - [Benchmark] - public void MinimumFatal() - { - levelSwitch.MinimumLevel = LogEventLevel.Fatal; - for (var i = 0; i < 1000; ++i) - { - log.Verbose("A verbose event!"); - log.Debug("A debug event!"); - log.Information("An info event!"); - log.Warning("A warm event!"); - log.Error("An error event!"); - log.Fatal("A fatal event!"); - } - } - } -} - \ No newline at end of file diff --git a/test/Serilog.PerformanceTests/NestedLoggerCreationBenchmark.cs b/test/Serilog.PerformanceTests/NestedLoggerCreationBenchmark.cs new file mode 100644 index 000000000..1c170d366 --- /dev/null +++ b/test/Serilog.PerformanceTests/NestedLoggerCreationBenchmark.cs @@ -0,0 +1,33 @@ +using BenchmarkDotNet.Attributes; +using Serilog.PerformanceTests.Support; + +namespace Serilog.PerformanceTests +{ + /// + /// Tests the cost creating a nested logger. + /// + public class NestedLoggerCreationBenchmark + { + ILogger log; + + [Setup] + public void Setup() + { + log = new LoggerConfiguration() + .WriteTo.Sink(new NullSink()) + .CreateLogger(); + } + + [Benchmark] + public void ForContextString() + { + log.ForContext("SourceContext", "Serilog.PerformanceTests"); + } + + [Benchmark] + public void ForContextType() + { + log.ForContext(); + } + } +} \ No newline at end of file diff --git a/test/Serilog.PerformanceTests/NestedLoggerLatencyBenchmark.cs b/test/Serilog.PerformanceTests/NestedLoggerLatencyBenchmark.cs new file mode 100644 index 000000000..537a60371 --- /dev/null +++ b/test/Serilog.PerformanceTests/NestedLoggerLatencyBenchmark.cs @@ -0,0 +1,42 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; +using Serilog; +using System; +using Serilog.PerformanceTests.Support; +using Xunit; +using Serilog.Events; + +namespace Serilog.PerformanceTests +{ + /// + /// Tests the overhead of writing through a nested logger. + /// + public class NestedLoggerLatencyBenchmark + { + ILogger _log, _nested; + readonly LogEvent _event = Some.InformationEvent(); + + [Setup] + public void Setup() + { + _log = new LoggerConfiguration() + .WriteTo.Sink(new NullSink()) + .CreateLogger(); + + _nested = _log.ForContext(); + } + + [Benchmark(Baseline = true)] + public void RootLogger() + { + _log.Write(_event); + } + + [Benchmark] + public void NestedLogger() + { + _nested.Write(_event); + } + } +} + \ No newline at end of file diff --git a/test/Serilog.PerformanceTests/MessageTemplateParserTests.cs b/test/Serilog.PerformanceTests/PipelineBenchmark.cs similarity index 60% rename from test/Serilog.PerformanceTests/MessageTemplateParserTests.cs rename to test/Serilog.PerformanceTests/PipelineBenchmark.cs index c124fe99f..6812f2345 100644 --- a/test/Serilog.PerformanceTests/MessageTemplateParserTests.cs +++ b/test/Serilog.PerformanceTests/PipelineBenchmark.cs @@ -15,7 +15,6 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; -using Serilog.Tests.Support; using Serilog; using Serilog.Events; using Serilog.Parsing; @@ -23,32 +22,36 @@ using System.Linq; using System.Collections; using System.Collections.Generic; +using Serilog.PerformanceTests.Support; using Xunit; namespace Serilog.PerformanceTests { - public class MessageTemplateParserTests - { - MessageTemplateParser _parser; - const string DefaultConsoleOutputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}"; + /// + /// Tests the cost of writing through the logging pipeline. + /// + public class PipelineBenchmark + { + ILogger _log; + Exception _exception; [Setup] public void Setup() { - _parser = new MessageTemplateParser(); - } + _exception = new Exception("An Error"); + _log = new LoggerConfiguration() + .WriteTo.Sink(new NullSink()) + .CreateLogger(); - [Benchmark(Baseline = true)] - public void Baseline() - { - var template = _parser.Parse(""); - } + // Ensure template is cached + _log.Information(_exception, "Hello, {Name}!", "World"); + } [Benchmark] - public void ParseDefaultConsoleOutputTemplate() + public void EmitLogEvent() { - var template = _parser.Parse(DefaultConsoleOutputTemplate); - } + _log.Information(_exception, "Hello, {Name}!", "World"); + } } } \ No newline at end of file diff --git a/test/Serilog.PerformanceTests/Support/NullSink.cs b/test/Serilog.PerformanceTests/Support/NullSink.cs new file mode 100644 index 000000000..14432a21a --- /dev/null +++ b/test/Serilog.PerformanceTests/Support/NullSink.cs @@ -0,0 +1,12 @@ +using Serilog.Events; +using Serilog.Core; + +namespace Serilog.PerformanceTests.Support +{ + class NullSink : ILogEventSink + { + public void Emit(LogEvent logEvent) + { + } + } +} diff --git a/test/Serilog.PerformanceTests/Support/Some.cs b/test/Serilog.PerformanceTests/Support/Some.cs new file mode 100644 index 000000000..07fbf5aab --- /dev/null +++ b/test/Serilog.PerformanceTests/Support/Some.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using Serilog.Events; +using Serilog.Parsing; + +namespace Serilog.PerformanceTests.Support +{ + static class Some + { + public static LogEvent InformationEvent() + { + return new LogEvent(DateTime.Now, LogEventLevel.Information, + null, new MessageTemplate(Enumerable.Empty()), Enumerable.Empty()); + } + + } +} diff --git a/test/Serilog.PerformanceTests/project.json b/test/Serilog.PerformanceTests/project.json index 65d6ef403..2ebb7562c 100755 --- a/test/Serilog.PerformanceTests/project.json +++ b/test/Serilog.PerformanceTests/project.json @@ -3,11 +3,9 @@ "dependencies": { "Serilog": { "target": "project" }, - "Serilog.Tests": { "target": "project" }, - "TestDummies": { "target": "project" }, "xunit": "2.1.0", "dotnet-test-xunit": "1.0.0-rc2-build10025", - "BenchmarkDotNet": "0.9.7-beta" + "BenchmarkDotNet": "0.9.9" }, "buildOptions": { "keyFile": "../../assets/Serilog.snk" From d48a13c3ea90ce15b5c5226ad1fdc2d10ea59f56 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 19 Aug 2016 17:17:46 +1000 Subject: [PATCH 12/13] Added some benchmark results for historical reference --- .../LevelControlBenchmark-report-github.md | 20 +++++++++++++++++++ ...ontextEnrichmentBenchmark-report-github.md | 20 +++++++++++++++++++ ...eTemplateParsingBenchmark-report-github.md | 18 +++++++++++++++++ ...edLoggerCreationBenchmark-report-github.md | 18 +++++++++++++++++ ...tedLoggerLatencyBenchmark-report-github.md | 18 +++++++++++++++++ .../PipelineBenchmark-report-github.md | 17 ++++++++++++++++ .../LevelControlBenchmark-report-github.md | 20 +++++++++++++++++++ ...ontextEnrichmentBenchmark-report-github.md | 20 +++++++++++++++++++ ...eTemplateParsingBenchmark-report-github.md | 18 +++++++++++++++++ ...edLoggerCreationBenchmark-report-github.md | 18 +++++++++++++++++ ...tedLoggerLatencyBenchmark-report-github.md | 18 +++++++++++++++++ .../PipelineBenchmark-report-github.md | 17 ++++++++++++++++ 12 files changed, 222 insertions(+) create mode 100644 results/net4.5.2/LevelControlBenchmark-report-github.md create mode 100644 results/net4.5.2/LogContextEnrichmentBenchmark-report-github.md create mode 100644 results/net4.5.2/MessageTemplateParsingBenchmark-report-github.md create mode 100644 results/net4.5.2/NestedLoggerCreationBenchmark-report-github.md create mode 100644 results/net4.5.2/NestedLoggerLatencyBenchmark-report-github.md create mode 100644 results/net4.5.2/PipelineBenchmark-report-github.md create mode 100644 results/netcoreapp1.0/LevelControlBenchmark-report-github.md create mode 100644 results/netcoreapp1.0/LogContextEnrichmentBenchmark-report-github.md create mode 100644 results/netcoreapp1.0/MessageTemplateParsingBenchmark-report-github.md create mode 100644 results/netcoreapp1.0/NestedLoggerCreationBenchmark-report-github.md create mode 100644 results/netcoreapp1.0/NestedLoggerLatencyBenchmark-report-github.md create mode 100644 results/netcoreapp1.0/PipelineBenchmark-report-github.md diff --git a/results/net4.5.2/LevelControlBenchmark-report-github.md b/results/net4.5.2/LevelControlBenchmark-report-github.md new file mode 100644 index 000000000..66c6b1310 --- /dev/null +++ b/results/net4.5.2/LevelControlBenchmark-report-github.md @@ -0,0 +1,20 @@ +```ini + +Host Process Environment Information: +BenchmarkDotNet.Core=v0.9.9.0 +OS=Microsoft Windows NT 6.2.9200.0 +Processor=Intel(R) Core(TM) i7-3720QM CPU 2.60GHz, ProcessorCount=8 +Frequency=2533306 ticks, Resolution=394.7411 ns, Timer=TSC +CLR=MS.NET 4.0.30319.42000, Arch=64-bit RELEASE [RyuJIT] +GC=Concurrent Workstation +JitModules=clrjit-v4.6.1080.0 + +Type=LevelControlBenchmark Mode=Throughput + +``` + Method | Median | StdDev | Scaled | Scaled-SD | +--------------- |----------- |---------- |------- |---------- | + Off | 4.1028 ns | 0.0534 ns | 1.00 | 0.00 | + LevelSwitchOff | 4.9870 ns | 0.0728 ns | 1.21 | 0.02 | + MinimumLevelOn | 14.6336 ns | 0.2279 ns | 3.58 | 0.07 | + LevelSwitchOn | 14.0402 ns | 0.2001 ns | 3.42 | 0.07 | diff --git a/results/net4.5.2/LogContextEnrichmentBenchmark-report-github.md b/results/net4.5.2/LogContextEnrichmentBenchmark-report-github.md new file mode 100644 index 000000000..962671da6 --- /dev/null +++ b/results/net4.5.2/LogContextEnrichmentBenchmark-report-github.md @@ -0,0 +1,20 @@ +```ini + +Host Process Environment Information: +BenchmarkDotNet.Core=v0.9.9.0 +OS=Microsoft Windows NT 6.2.9200.0 +Processor=Intel(R) Core(TM) i7-3720QM CPU 2.60GHz, ProcessorCount=8 +Frequency=2533306 ticks, Resolution=394.7411 ns, Timer=TSC +CLR=MS.NET 4.0.30319.42000, Arch=64-bit RELEASE [RyuJIT] +GC=Concurrent Workstation +JitModules=clrjit-v4.6.1080.0 + +Type=LogContextEnrichmentBenchmark Mode=Throughput + +``` + Method | Median | StdDev | Scaled | Scaled-SD | +--------------------- |------------ |----------- |------- |---------- | + Bare | 14.5935 ns | 0.2010 ns | 1.00 | 0.00 | + PushProperty | 384.9070 ns | 2.4957 ns | 26.61 | 0.41 | + PushPropertyNested | 778.8093 ns | 51.8017 ns | 55.46 | 3.59 | + PushPropertyEnriched | 598.1650 ns | 12.3394 ns | 41.52 | 1.01 | diff --git a/results/net4.5.2/MessageTemplateParsingBenchmark-report-github.md b/results/net4.5.2/MessageTemplateParsingBenchmark-report-github.md new file mode 100644 index 000000000..086cdfd5e --- /dev/null +++ b/results/net4.5.2/MessageTemplateParsingBenchmark-report-github.md @@ -0,0 +1,18 @@ +```ini + +Host Process Environment Information: +BenchmarkDotNet.Core=v0.9.9.0 +OS=Microsoft Windows NT 6.2.9200.0 +Processor=Intel(R) Core(TM) i7-3720QM CPU 2.60GHz, ProcessorCount=8 +Frequency=2533306 ticks, Resolution=394.7411 ns, Timer=TSC +CLR=MS.NET 4.0.30319.42000, Arch=64-bit RELEASE [RyuJIT] +GC=Concurrent Workstation +JitModules=clrjit-v4.6.1080.0 + +Type=MessageTemplateParsingBenchmark Mode=Throughput + +``` + Method | Median | StdDev | Scaled | Scaled-SD | +----------------------------- |-------------- |----------- |------- |---------- | + EmptyTemplate | 183.3209 ns | 3.9928 ns | 1.00 | 0.00 | + DefaultConsoleOutputTemplate | 2,636.2172 ns | 77.3304 ns | 14.40 | 0.50 | diff --git a/results/net4.5.2/NestedLoggerCreationBenchmark-report-github.md b/results/net4.5.2/NestedLoggerCreationBenchmark-report-github.md new file mode 100644 index 000000000..620c50bb9 --- /dev/null +++ b/results/net4.5.2/NestedLoggerCreationBenchmark-report-github.md @@ -0,0 +1,18 @@ +```ini + +Host Process Environment Information: +BenchmarkDotNet.Core=v0.9.9.0 +OS=Microsoft Windows NT 6.2.9200.0 +Processor=Intel(R) Core(TM) i7-3720QM CPU 2.60GHz, ProcessorCount=8 +Frequency=2533306 ticks, Resolution=394.7411 ns, Timer=TSC +CLR=MS.NET 4.0.30319.42000, Arch=64-bit RELEASE [RyuJIT] +GC=Concurrent Workstation +JitModules=clrjit-v4.6.1080.0 + +Type=NestedLoggerCreationBenchmark Mode=Throughput + +``` + Method | Median | StdDev | +----------------- |------------ |---------- | + ForContextString | 91.0471 ns | 0.5325 ns | + ForContextType | 159.5909 ns | 1.3952 ns | diff --git a/results/net4.5.2/NestedLoggerLatencyBenchmark-report-github.md b/results/net4.5.2/NestedLoggerLatencyBenchmark-report-github.md new file mode 100644 index 000000000..b2715a85b --- /dev/null +++ b/results/net4.5.2/NestedLoggerLatencyBenchmark-report-github.md @@ -0,0 +1,18 @@ +```ini + +Host Process Environment Information: +BenchmarkDotNet.Core=v0.9.9.0 +OS=Microsoft Windows NT 6.2.9200.0 +Processor=Intel(R) Core(TM) i7-3720QM CPU 2.60GHz, ProcessorCount=8 +Frequency=2533306 ticks, Resolution=394.7411 ns, Timer=TSC +CLR=MS.NET 4.0.30319.42000, Arch=64-bit RELEASE [RyuJIT] +GC=Concurrent Workstation +JitModules=clrjit-v4.6.1080.0 + +Type=NestedLoggerLatencyBenchmark Mode=Throughput + +``` + Method | Median | StdDev | Scaled | Scaled-SD | +------------- |----------- |---------- |------- |---------- | + RootLogger | 14.5315 ns | 0.6035 ns | 1.00 | 0.00 | + NestedLogger | 52.6109 ns | 1.0453 ns | 3.60 | 0.15 | diff --git a/results/net4.5.2/PipelineBenchmark-report-github.md b/results/net4.5.2/PipelineBenchmark-report-github.md new file mode 100644 index 000000000..659d6d488 --- /dev/null +++ b/results/net4.5.2/PipelineBenchmark-report-github.md @@ -0,0 +1,17 @@ +```ini + +Host Process Environment Information: +BenchmarkDotNet.Core=v0.9.9.0 +OS=Microsoft Windows NT 6.2.9200.0 +Processor=Intel(R) Core(TM) i7-3720QM CPU 2.60GHz, ProcessorCount=8 +Frequency=2533306 ticks, Resolution=394.7411 ns, Timer=TSC +CLR=MS.NET 4.0.30319.42000, Arch=64-bit RELEASE [RyuJIT] +GC=Concurrent Workstation +JitModules=clrjit-v4.6.1080.0 + +Type=PipelineBenchmark Mode=Throughput + +``` + Method | Median | StdDev | +------------- |------------ |---------- | + EmitLogEvent | 419.2931 ns | 4.3815 ns | diff --git a/results/netcoreapp1.0/LevelControlBenchmark-report-github.md b/results/netcoreapp1.0/LevelControlBenchmark-report-github.md new file mode 100644 index 000000000..4e18aec9e --- /dev/null +++ b/results/netcoreapp1.0/LevelControlBenchmark-report-github.md @@ -0,0 +1,20 @@ +```ini + +Host Process Environment Information: +BenchmarkDotNet.Core=v0.9.9.0 +OS=Windows +Processor=?, ProcessorCount=8 +Frequency=2533306 ticks, Resolution=394.7411 ns, Timer=TSC +CLR=CORE, Arch=64-bit ? [RyuJIT] +GC=Concurrent Workstation +dotnet cli version: 1.0.0-preview2-003121 + +Type=LevelControlBenchmark Mode=Throughput + +``` + Method | Median | StdDev | Scaled | Scaled-SD | +--------------- |----------- |---------- |------- |---------- | + Off | 4.1095 ns | 0.1013 ns | 1.00 | 0.00 | + LevelSwitchOff | 4.9570 ns | 0.0807 ns | 1.20 | 0.03 | + MinimumLevelOn | 14.5884 ns | 0.1821 ns | 3.54 | 0.10 | + LevelSwitchOn | 13.9769 ns | 0.1802 ns | 3.38 | 0.09 | diff --git a/results/netcoreapp1.0/LogContextEnrichmentBenchmark-report-github.md b/results/netcoreapp1.0/LogContextEnrichmentBenchmark-report-github.md new file mode 100644 index 000000000..918e58f9f --- /dev/null +++ b/results/netcoreapp1.0/LogContextEnrichmentBenchmark-report-github.md @@ -0,0 +1,20 @@ +```ini + +Host Process Environment Information: +BenchmarkDotNet.Core=v0.9.9.0 +OS=Windows +Processor=?, ProcessorCount=8 +Frequency=2533306 ticks, Resolution=394.7411 ns, Timer=TSC +CLR=CORE, Arch=64-bit ? [RyuJIT] +GC=Concurrent Workstation +dotnet cli version: 1.0.0-preview2-003121 + +Type=LogContextEnrichmentBenchmark Mode=Throughput + +``` + Method | Median | StdDev | Scaled | Scaled-SD | +--------------------- |------------ |----------- |------- |---------- | + Bare | 14.3441 ns | 0.2826 ns | 1.00 | 0.00 | + PushProperty | 450.5621 ns | 3.2270 ns | 31.42 | 0.65 | + PushPropertyNested | 920.1179 ns | 45.1624 ns | 63.81 | 3.30 | + PushPropertyEnriched | 730.4018 ns | 4.5812 ns | 50.92 | 1.03 | diff --git a/results/netcoreapp1.0/MessageTemplateParsingBenchmark-report-github.md b/results/netcoreapp1.0/MessageTemplateParsingBenchmark-report-github.md new file mode 100644 index 000000000..99f76d5ca --- /dev/null +++ b/results/netcoreapp1.0/MessageTemplateParsingBenchmark-report-github.md @@ -0,0 +1,18 @@ +```ini + +Host Process Environment Information: +BenchmarkDotNet.Core=v0.9.9.0 +OS=Windows +Processor=?, ProcessorCount=8 +Frequency=2533306 ticks, Resolution=394.7411 ns, Timer=TSC +CLR=CORE, Arch=64-bit ? [RyuJIT] +GC=Concurrent Workstation +dotnet cli version: 1.0.0-preview2-003121 + +Type=MessageTemplateParsingBenchmark Mode=Throughput + +``` + Method | Median | StdDev | Scaled | Scaled-SD | +----------------------------- |-------------- |----------- |------- |---------- | + EmptyTemplate | 188.2760 ns | 2.4773 ns | 1.00 | 0.00 | + DefaultConsoleOutputTemplate | 2,753.6243 ns | 22.7107 ns | 14.54 | 0.22 | diff --git a/results/netcoreapp1.0/NestedLoggerCreationBenchmark-report-github.md b/results/netcoreapp1.0/NestedLoggerCreationBenchmark-report-github.md new file mode 100644 index 000000000..39ae09e76 --- /dev/null +++ b/results/netcoreapp1.0/NestedLoggerCreationBenchmark-report-github.md @@ -0,0 +1,18 @@ +```ini + +Host Process Environment Information: +BenchmarkDotNet.Core=v0.9.9.0 +OS=Windows +Processor=?, ProcessorCount=8 +Frequency=2533306 ticks, Resolution=394.7411 ns, Timer=TSC +CLR=CORE, Arch=64-bit ? [RyuJIT] +GC=Concurrent Workstation +dotnet cli version: 1.0.0-preview2-003121 + +Type=NestedLoggerCreationBenchmark Mode=Throughput + +``` + Method | Median | StdDev | +----------------- |------------ |---------- | + ForContextString | 92.1957 ns | 0.8786 ns | + ForContextType | 155.5276 ns | 0.8364 ns | diff --git a/results/netcoreapp1.0/NestedLoggerLatencyBenchmark-report-github.md b/results/netcoreapp1.0/NestedLoggerLatencyBenchmark-report-github.md new file mode 100644 index 000000000..bceb35026 --- /dev/null +++ b/results/netcoreapp1.0/NestedLoggerLatencyBenchmark-report-github.md @@ -0,0 +1,18 @@ +```ini + +Host Process Environment Information: +BenchmarkDotNet.Core=v0.9.9.0 +OS=Windows +Processor=?, ProcessorCount=8 +Frequency=2533306 ticks, Resolution=394.7411 ns, Timer=TSC +CLR=CORE, Arch=64-bit ? [RyuJIT] +GC=Concurrent Workstation +dotnet cli version: 1.0.0-preview2-003121 + +Type=NestedLoggerLatencyBenchmark Mode=Throughput + +``` + Method | Median | StdDev | Scaled | Scaled-SD | +------------- |----------- |---------- |------- |---------- | + RootLogger | 14.0131 ns | 0.2212 ns | 1.00 | 0.00 | + NestedLogger | 59.4502 ns | 1.3198 ns | 4.23 | 0.11 | diff --git a/results/netcoreapp1.0/PipelineBenchmark-report-github.md b/results/netcoreapp1.0/PipelineBenchmark-report-github.md new file mode 100644 index 000000000..37743f9ba --- /dev/null +++ b/results/netcoreapp1.0/PipelineBenchmark-report-github.md @@ -0,0 +1,17 @@ +```ini + +Host Process Environment Information: +BenchmarkDotNet.Core=v0.9.9.0 +OS=Windows +Processor=?, ProcessorCount=8 +Frequency=2533306 ticks, Resolution=394.7411 ns, Timer=TSC +CLR=CORE, Arch=64-bit ? [RyuJIT] +GC=Concurrent Workstation +dotnet cli version: 1.0.0-preview2-003121 + +Type=PipelineBenchmark Mode=Throughput + +``` + Method | Median | StdDev | +------------- |------------ |---------- | + EmitLogEvent | 712.9876 ns | 7.2272 ns | From ff63c8e8d93c714d5c68bf1bbf99b5300b15234f Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 26 Aug 2016 15:56:41 +1000 Subject: [PATCH 13/13] Missing `readonly` modifier --- src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs b/src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs index 9381f7121..061222d55 100644 --- a/src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs +++ b/src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs @@ -19,11 +19,11 @@ namespace Serilog.Configuration { /// - /// Controls sink configuration. + /// Controls audit sink configuration. /// public class LoggerAuditSinkConfiguration { - LoggerSinkConfiguration _sinkConfiguration; + readonly LoggerSinkConfiguration _sinkConfiguration; internal LoggerAuditSinkConfiguration(LoggerConfiguration loggerConfiguration, Action addSink, Action applyInheritedConfiguration) {