From b6abdac3527d3b7dfa075be929bdf2cb587d7faf Mon Sep 17 00:00:00 2001 From: Wilco Boshoff Date: Thu, 7 Feb 2019 15:30:26 +0200 Subject: [PATCH 01/22] Upgraded Microsoft.Extensions.Logging NuGet packages to 2.0.0 --- global.json | 2 +- .../Extensions/Logging/SerilogLoggerProvider.cs | 2 +- .../Serilog.Extensions.Logging.csproj | 12 ++++++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/global.json b/global.json index 73bdd84..b3bc9e1 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "2.0.0-preview2-006497" + "version": "2.1.0" } } diff --git a/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs index 0a8910e..4af07f9 100644 --- a/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs +++ b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs @@ -66,7 +66,7 @@ public IDisposable BeginScope(T state) // The outermost scope pushes and pops the Serilog `LogContext` - once // this enricher is on the stack, the `CurrentScope` property takes care // of the rest of the `BeginScope()` stack. - var popSerilogContext = LogContext.PushProperties(this); + var popSerilogContext = LogContext.Push(this); return new SerilogLoggerScope(this, state, popSerilogContext); } diff --git a/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj b/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj index d0adcc8..1607a12 100644 --- a/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj +++ b/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj @@ -23,17 +23,25 @@ - + - + + + + + + + + + $(DefineConstants);ASYNCLOCAL From 7030b19ebbd195f8ee0f33a254e2b14d97e56f03 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 8 Feb 2019 11:35:55 +1000 Subject: [PATCH 02/22] Proof-of-concept AddProvider() infrastructure --- samples/Sample/Program.cs | 27 +++++-- samples/Sample/Sample.csproj | 3 +- .../Extensions/Logging/LevelMapping.cs | 50 ++++++++++++ .../Logging/LoggerProviderCollection.cs | 67 ++++++++++++++++ .../Logging/LoggerProviderCollectionSink.cs | 61 +++++++++++++++ .../Extensions/Logging/SerilogLogger.cs | 25 +----- .../Logging/SerilogLoggerFactory.cs | 76 +++++++++++++++++++ .../Logging/SerilogLoggerProvider.cs | 4 +- .../LoggerSinkConfigurationExtensions.cs | 39 ++++++++++ .../Serilog.Extensions.Logging.csproj | 4 +- 10 files changed, 321 insertions(+), 35 deletions(-) create mode 100644 src/Serilog.Extensions.Logging/Extensions/Logging/LevelMapping.cs create mode 100644 src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollection.cs create mode 100644 src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollectionSink.cs create mode 100644 src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerFactory.cs create mode 100644 src/Serilog.Extensions.Logging/LoggerSinkConfigurationExtensions.cs diff --git a/samples/Sample/Program.cs b/samples/Sample/Program.cs index 7f95bfa..ac82eb9 100644 --- a/samples/Sample/Program.cs +++ b/samples/Sample/Program.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Serilog; +using Serilog.Extensions.Logging; namespace Sample { @@ -9,19 +10,29 @@ public class Program { public static void Main(string[] args) { + // Creating a `LoggerProviderCollection` lets Serilog optionally write + // events through other dynamically-added MEL ILoggerProviders. + var providers = new LoggerProviderCollection(); + Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() - .WriteTo.LiterateConsole() + .WriteTo.Console() + .WriteTo.Providers(providers) .CreateLogger(); - var services = new ServiceCollection() - .AddLogging(builder => - { - builder.AddSerilog(); - }); + var services = new ServiceCollection(); + services.AddSingleton(sc => + { + // Add providers already registered through IoC + foreach (var provider in sc.GetServices()) + providers.AddProvider(provider); + + return new SerilogLoggerFactory(null, true, providers); + }); + + services.AddLogging(l => l.AddConsole()); var serviceProvider = services.BuildServiceProvider(); - // getting the logger using the class's name is conventional var logger = serviceProvider.GetRequiredService>(); var startTime = DateTimeOffset.UtcNow; @@ -57,6 +68,8 @@ public static void Main(string[] args) logger.LogInformation("{Result,-10:l}{StartTime,15:l}{EndTime,15:l}{Duration,15:l}", "RESULT", "START TIME", "END TIME", "DURATION(ms)"); logger.LogInformation("{Result,-10:l}{StartTime,15:l}{EndTime,15:l}{Duration,15:l}", "------", "----- ----", "--- ----", "------------"); logger.LogInformation("{Result,-10:l}{StartTime,15:mm:s tt}{EndTime,15:mm:s tt}{Duration,15}", "SUCCESS", startTime, endTime, (endTime - startTime).TotalMilliseconds); + + serviceProvider.Dispose(); } } } diff --git a/samples/Sample/Sample.csproj b/samples/Sample/Sample.csproj index 5c34526..4bae7c1 100644 --- a/samples/Sample/Sample.csproj +++ b/samples/Sample/Sample.csproj @@ -14,7 +14,8 @@ - + + \ No newline at end of file diff --git a/src/Serilog.Extensions.Logging/Extensions/Logging/LevelMapping.cs b/src/Serilog.Extensions.Logging/Extensions/Logging/LevelMapping.cs new file mode 100644 index 0000000..1fd5cd1 --- /dev/null +++ b/src/Serilog.Extensions.Logging/Extensions/Logging/LevelMapping.cs @@ -0,0 +1,50 @@ +using Microsoft.Extensions.Logging; +using Serilog.Events; + +namespace Serilog.Extensions.Logging +{ + static class LevelMapping + { + public static LogEventLevel ToSerilogLevel(LogLevel logLevel) + { + switch (logLevel) + { + case LogLevel.Critical: + return LogEventLevel.Fatal; + case LogLevel.Error: + return LogEventLevel.Error; + case LogLevel.Warning: + return LogEventLevel.Warning; + case LogLevel.Information: + return LogEventLevel.Information; + case LogLevel.Debug: + return LogEventLevel.Debug; + // ReSharper disable once RedundantCaseLabel + case LogLevel.Trace: + default: + return LogEventLevel.Verbose; + } + } + + public static LogLevel ToExtensionsLevel(LogEventLevel logEventLevel) + { + switch (logEventLevel) + { + case LogEventLevel.Fatal: + return LogLevel.Critical; + case LogEventLevel.Error: + return LogLevel.Error; + case LogEventLevel.Warning: + return LogLevel.Warning; + case LogEventLevel.Information: + return LogLevel.Information; + case LogEventLevel.Debug: + return LogLevel.Debug; + // ReSharper disable once RedundantCaseLabel + case LogEventLevel.Verbose: + default: + return LogLevel.Trace; + } + } + } +} diff --git a/src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollection.cs b/src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollection.cs new file mode 100644 index 0000000..6ecb000 --- /dev/null +++ b/src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollection.cs @@ -0,0 +1,67 @@ +// Copyright 2019 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 System.Threading; +using Microsoft.Extensions.Logging; + +namespace Serilog.Extensions.Logging +{ + /// + /// A dynamically-modifiable collection of s. + /// + public class LoggerProviderCollection : IDisposable + { + volatile ILoggerProvider[] _providers = new ILoggerProvider[0]; + + /// + /// Add to the collection. + /// + /// A logger provider. + public void AddProvider(ILoggerProvider provider) + { + if (provider == null) throw new ArgumentNullException(nameof(provider)); + + var existing = _providers; + var added = existing.Concat(new[] {provider}).ToArray(); + +#pragma warning disable 420 // ref to a volatile field + while (Interlocked.CompareExchange(ref _providers, added, existing) != existing) +#pragma warning restore 420 + { + existing = _providers; + added = existing.Concat(new[] { provider }).ToArray(); + } + } + + /// + /// Get the currently-active providers. + /// + /// + /// If the collection has been disposed, we'll leave the individual + /// providers with the job of throwing . + /// + public IEnumerable Providers => _providers; + + /// + public void Dispose() + { + foreach (var provider in _providers) + provider.Dispose(); + } + } +} diff --git a/src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollectionSink.cs b/src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollectionSink.cs new file mode 100644 index 0000000..7153354 --- /dev/null +++ b/src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollectionSink.cs @@ -0,0 +1,61 @@ +// Copyright 2019 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 Microsoft.Extensions.Logging; +using Serilog.Core; +using Serilog.Events; + +namespace Serilog.Extensions.Logging +{ + class LoggerProviderCollectionSink : ILogEventSink, IDisposable + { + readonly LoggerProviderCollection _providers; + + public LoggerProviderCollectionSink(LoggerProviderCollection providers) + { + _providers = providers ?? throw new ArgumentNullException(nameof(providers)); + } + + public void Emit(LogEvent logEvent) + { + string categoryName = null; + + if (logEvent.Properties.TryGetValue("SourceContext", out var sourceContextProperty) && + sourceContextProperty is ScalarValue sourceContextValue && + sourceContextValue.Value is string sourceContext) + { + categoryName = sourceContext; + } + + foreach (var provider in _providers.Providers) + { + var logger = provider.CreateLogger(categoryName); + + + logger.Log( + LevelMapping.ToExtensionsLevel(logEvent.Level), + default(EventId), + logEvent, + logEvent.Exception, + (s, ex) => s.RenderMessage()); + } + } + + public void Dispose() + { + _providers.Dispose(); + } + } +} diff --git a/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLogger.cs b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLogger.cs index 5dff16c..e81f79e 100644 --- a/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLogger.cs +++ b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLogger.cs @@ -39,7 +39,7 @@ public SerilogLogger( public bool IsEnabled(LogLevel logLevel) { - return _logger.IsEnabled(ConvertLevel(logLevel)); + return _logger.IsEnabled(LevelMapping.ToSerilogLevel(logLevel)); } public IDisposable BeginScope(TState state) @@ -49,7 +49,7 @@ public IDisposable BeginScope(TState state) public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { - var level = ConvertLevel(logLevel); + var level = LevelMapping.ToSerilogLevel(logLevel); if (!_logger.IsEnabled(level)) { return; @@ -133,27 +133,6 @@ static object AsLoggableValue(TState state, Func(2); diff --git a/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerFactory.cs b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerFactory.cs new file mode 100644 index 0000000..cf25f06 --- /dev/null +++ b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerFactory.cs @@ -0,0 +1,76 @@ +// Copyright 2019 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 Microsoft.Extensions.Logging; +using Serilog.Debugging; + +namespace Serilog.Extensions.Logging +{ + /// + /// A complete Serilog-backed implementation of the .NET Core logging infrastructure. + /// + public class SerilogLoggerFactory : ILoggerFactory + { + readonly LoggerProviderCollection _providerCollection; + readonly SerilogLoggerProvider _provider; + + /// + /// Initializes a new instance of the class. + /// + /// The Serilog logger; if not supplied, the static will be used. + /// When true, dispose when the framework disposes the provider. If the + /// logger is not specified but is true, the method will be + /// called on the static class instead. + /// A , for use with WriteTo.Providers(). + public SerilogLoggerFactory(ILogger logger = null, bool dispose = false, LoggerProviderCollection providerCollection = null) + { + _provider = new SerilogLoggerProvider(logger, dispose); + _providerCollection = providerCollection; + } + + /// + /// Disposes the provider. + /// + public void Dispose() + { + _provider.Dispose(); + } + + /// + /// Creates a new instance. + /// + /// The category name for messages produced by the logger. + /// + /// The . + /// + public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName) + { + return _provider.CreateLogger(categoryName); + } + + /// + /// Adds an to the logging system. + /// + /// The . + public void AddProvider(ILoggerProvider provider) + { + if (provider == null) throw new ArgumentNullException(nameof(provider)); + if (_providerCollection != null) + _providerCollection.AddProvider(provider); + else + SelfLog.WriteLine("Ignoring added logger provider {0}", provider); + } + } +} diff --git a/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs index 0a8910e..b4905ff 100644 --- a/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs +++ b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs @@ -57,7 +57,7 @@ public FrameworkLogger CreateLogger(string name) return new SerilogLogger(this, _logger, name); } - /// + /// public IDisposable BeginScope(T state) { if (CurrentScope != null) @@ -66,7 +66,7 @@ public IDisposable BeginScope(T state) // The outermost scope pushes and pops the Serilog `LogContext` - once // this enricher is on the stack, the `CurrentScope` property takes care // of the rest of the `BeginScope()` stack. - var popSerilogContext = LogContext.PushProperties(this); + var popSerilogContext = LogContext.Push(this); return new SerilogLoggerScope(this, state, popSerilogContext); } diff --git a/src/Serilog.Extensions.Logging/LoggerSinkConfigurationExtensions.cs b/src/Serilog.Extensions.Logging/LoggerSinkConfigurationExtensions.cs new file mode 100644 index 0000000..8524828 --- /dev/null +++ b/src/Serilog.Extensions.Logging/LoggerSinkConfigurationExtensions.cs @@ -0,0 +1,39 @@ +// Copyright 2019 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.Configuration; +using Serilog.Extensions.Logging; + +namespace Serilog +{ + /// + /// Extensions for . + /// + public static class LoggerSinkConfigurationExtensions + { + /// + /// Write Serilog events to the providers in . + /// + /// The `WriteTo` object. + /// A to write events to. + /// A to allow method chaining. + public static LoggerConfiguration Providers(this LoggerSinkConfiguration configuration, LoggerProviderCollection providers) + { + if (configuration == null) throw new ArgumentNullException(nameof(configuration)); + if (providers == null) throw new ArgumentNullException(nameof(providers)); + return configuration.Sink(new LoggerProviderCollectionSink(providers)); + } + } +} diff --git a/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj b/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj index d0adcc8..7c97dc7 100644 --- a/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj +++ b/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj @@ -1,4 +1,4 @@ - + Low-level Serilog provider for Microsoft.Extensions.Logging @@ -23,7 +23,7 @@ - + From e94a2403cf453f417a06696ae69487d47fd5dfe3 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 8 Feb 2019 11:51:27 +1000 Subject: [PATCH 03/22] Lean a bit more on IoC to wire up the factory and providers --- samples/Sample/Program.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/samples/Sample/Program.cs b/samples/Sample/Program.cs index ac82eb9..03014b7 100644 --- a/samples/Sample/Program.cs +++ b/samples/Sample/Program.cs @@ -21,13 +21,17 @@ public static void Main(string[] args) .CreateLogger(); var services = new ServiceCollection(); + + services.AddSingleton(providers); services.AddSingleton(sc => { - // Add providers already registered through IoC + var providerCollection = sc.GetService(); + var factory = new SerilogLoggerFactory(null, true, providerCollection); + foreach (var provider in sc.GetServices()) - providers.AddProvider(provider); + factory.AddProvider(provider); - return new SerilogLoggerFactory(null, true, providers); + return factory; }); services.AddLogging(l => l.AddConsole()); From 5c7ee2af886a1594db29f6744e1cf500ee065017 Mon Sep 17 00:00:00 2001 From: Wilco Boshoff Date: Fri, 8 Feb 2019 08:15:38 +0200 Subject: [PATCH 04/22] Removed all netcore1.3 references and dependent code. Assumed netcore2.0 dependency from here on out. --- build.sh | 1 - global.json | 2 +- .../Logging/SerilogLoggerProvider.cs | 22 ------------ .../Serilog.Extensions.Logging.csproj | 34 +++---------------- .../Serilog.Extensions.Logging.Tests.csproj | 6 ++-- 5 files changed, 7 insertions(+), 58 deletions(-) diff --git a/build.sh b/build.sh index dc51fc0..6d1ff38 100644 --- a/build.sh +++ b/build.sh @@ -3,7 +3,6 @@ dotnet --info dotnet restore for path in src/**/*.csproj; do - dotnet build -f netstandard1.3 -c Release ${path} dotnet build -f netstandard2.0 -c Release ${path} done diff --git a/global.json b/global.json index b3bc9e1..110af60 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "2.1.0" + "version": "2.0.0" } } diff --git a/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs index 4af07f9..2e9dbfe 100644 --- a/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs +++ b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs @@ -2,12 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -#if ASYNCLOCAL using System.Threading; -#else -using System.Runtime.Remoting; -using System.Runtime.Remoting.Messaging; -#endif using Microsoft.Extensions.Logging; using Serilog.Core; using Serilog.Events; @@ -93,7 +88,6 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) } } -#if ASYNCLOCAL readonly AsyncLocal _value = new AsyncLocal(); internal SerilogLoggerScope CurrentScope @@ -107,22 +101,6 @@ internal SerilogLoggerScope CurrentScope _value.Value = value; } } -#else - readonly string _currentScopeKey = nameof(SerilogLoggerScope) + "#" + Guid.NewGuid().ToString("n"); - - internal SerilogLoggerScope CurrentScope - { - get - { - var objectHandle = CallContext.LogicalGetData(_currentScopeKey) as ObjectHandle; - return objectHandle?.Unwrap() as SerilogLoggerScope; - } - set - { - CallContext.LogicalSetData(_currentScopeKey, new ObjectHandle(value)); - } - } -#endif /// public void Dispose() diff --git a/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj b/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj index 1607a12..0cc2562 100644 --- a/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj +++ b/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj @@ -4,7 +4,7 @@ Low-level Serilog provider for Microsoft.Extensions.Logging 2.0.1 Microsoft;Serilog Contributors - net45;net46;net461;netstandard1.3;netstandard2.0 + net461;netstandard2.0 true true Serilog.Extensions.Logging @@ -24,38 +24,12 @@ - - - - - - - - - - - - - - - + - - $(DefineConstants);ASYNCLOCAL - - - - $(DefineConstants);ASYNCLOCAL;LOGGING_BUILDER - - - - $(DefineConstants);ASYNCLOCAL - - - - $(DefineConstants);ASYNCLOCAL;LOGGING_BUILDER + + $(DefineConstants);LOGGING_BUILDER diff --git a/test/Serilog.Extensions.Logging.Tests/Serilog.Extensions.Logging.Tests.csproj b/test/Serilog.Extensions.Logging.Tests/Serilog.Extensions.Logging.Tests.csproj index 54eee14..c3ea6fb 100644 --- a/test/Serilog.Extensions.Logging.Tests/Serilog.Extensions.Logging.Tests.csproj +++ b/test/Serilog.Extensions.Logging.Tests/Serilog.Extensions.Logging.Tests.csproj @@ -1,15 +1,13 @@  - netcoreapp1.1;netcoreapp2.0;net46;net461 + netcoreapp2.0;net461 Serilog.Extensions.Logging.Tests ../../assets/Serilog.snk true true Serilog.Extensions.Logging.Tests true - $(PackageTargetFallback);dnxcore50;portable-net45+win8 - 1.0.4 @@ -22,7 +20,7 @@ - + From 1ffaa7b4579c44a74456d0de69555fe6e6cdd9a3 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 14 Mar 2019 22:59:28 +1000 Subject: [PATCH 05/22] Use FormattedLogValues as the event state --- .../Logging/LoggerProviderCollectionSink.cs | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollectionSink.cs b/src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollectionSink.cs index 7153354..dca9826 100644 --- a/src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollectionSink.cs +++ b/src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollectionSink.cs @@ -13,9 +13,12 @@ // limitations under the License. using System; +using System.Linq; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Internal; using Serilog.Core; using Serilog.Events; +using Serilog.Parsing; namespace Serilog.Extensions.Logging { @@ -39,17 +42,31 @@ sourceContextProperty is ScalarValue sourceContextValue && categoryName = sourceContext; } + // Allocates like mad, but first make it work, then make it work fast ;-) + var flv = new FormattedLogValues( + logEvent.MessageTemplate.Text, + logEvent.MessageTemplate.Tokens + .OfType() + .Select(p => + { + if (!logEvent.Properties.TryGetValue(p.PropertyName, out var value)) + return null; + if (value is ScalarValue sv) + return sv.Value; + return value; + }) + .ToArray()); + foreach (var provider in _providers.Providers) { var logger = provider.CreateLogger(categoryName); - logger.Log( LevelMapping.ToExtensionsLevel(logEvent.Level), default(EventId), - logEvent, + flv, logEvent.Exception, - (s, ex) => s.RenderMessage()); + (s, e) => s.ToString()); } } From 507cc4b24b7b2b1db4893e3a8a68832896942c6f Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 24 Apr 2019 06:52:37 +1000 Subject: [PATCH 06/22] Dev version bump [skip ci] --- .../Serilog.Extensions.Logging.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj b/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj index 3501e2c..dbf3073 100644 --- a/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj +++ b/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj @@ -2,7 +2,7 @@ Low-level Serilog provider for Microsoft.Extensions.Logging - 2.0.4 + 2.0.5 Microsoft;Serilog Contributors net45;net46;net461;netstandard1.3;netstandard2.0 true From 3c27e7edc85250a91da1a5ee78f46c51f8a6c377 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Wed, 24 Apr 2019 12:46:07 +1000 Subject: [PATCH 07/22] move to current sdk --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 73bdd84..7ed2ef7 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "2.0.0-preview2-006497" + "version": "2.2.202" } } From cf578084c9caab7434633d0af72ada3ecef1e4df Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 25 Apr 2019 13:41:57 +1000 Subject: [PATCH 08/22] image: Visual Studio 2019 Preview --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 0f0fb46..0847757 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,6 @@ version: '{build}' skip_tags: true -image: Visual Studio 2017 Preview +image: Visual Studio 2019 Preview configuration: Release install: - ps: mkdir -Force ".\build\" | Out-Null From 6c36af7d5b50ec522dc09c7c0ddc9a39b5b8c88b Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 25 Apr 2019 13:47:08 +1000 Subject: [PATCH 09/22] Update global.json --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 7ed2ef7..6b64107 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "2.2.202" + "version": "2.2.103" } } From 31d7194abaf09bf68fbfe734f06b16d821cb5c0f Mon Sep 17 00:00:00 2001 From: Matthew Erbs Date: Fri, 3 May 2019 07:52:53 +1000 Subject: [PATCH 10/22] Update NuGet API Key --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 0847757..6b98e44 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,7 +16,7 @@ artifacts: deploy: - provider: NuGet api_key: - secure: bd9z4P73oltOXudAjPehwp9iDKsPtC+HbgshOrSgoyQKr5xVK+bxJQngrDJkHdY8 + secure: N59tiJECUYpip6tEn0xvdmDAEiP9SIzyLEFLpwiigm/8WhJvBNs13QxzT1/3/JW/ skip_symbols: true on: branch: /^(master|dev)$/ From ad31fe81732925422250e362786ce15dc84f9e6b Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 16 May 2019 13:56:01 +1000 Subject: [PATCH 11/22] Merge upstream/dev --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 6b64107..7ed2ef7 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "2.2.103" + "version": "2.2.202" } } From 6846f43aab8ac9419d61e3c0f9cffa110f6f4e5a Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 16 May 2019 14:57:15 +1000 Subject: [PATCH 12/22] SerilogLogValues to clean up provision of TState; re-version as 3.0.0-* and limit dependencies --- samples/Sample/Sample.csproj | 2 +- .../Extensions/Logging/LevelMapping.cs | 16 ++++- .../Logging/LoggerProviderCollectionSink.cs | 26 ++------ .../Extensions/Logging/SerilogLogValues.cs | 62 +++++++++++++++++++ .../Serilog.Extensions.Logging.csproj | 23 +------ .../Serilog.Extensions.Logging.Tests.csproj | 9 +-- .../SerilogLogValuesTests.cs | 53 ++++++++++++++++ 7 files changed, 139 insertions(+), 52 deletions(-) create mode 100644 src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLogValues.cs create mode 100644 test/Serilog.Extensions.Logging.Tests/SerilogLogValuesTests.cs diff --git a/samples/Sample/Sample.csproj b/samples/Sample/Sample.csproj index 4bae7c1..c557b8a 100644 --- a/samples/Sample/Sample.csproj +++ b/samples/Sample/Sample.csproj @@ -1,7 +1,7 @@  - net461;netcoreapp2.0 + netcoreapp2.0 Sample Exe Sample diff --git a/src/Serilog.Extensions.Logging/Extensions/Logging/LevelMapping.cs b/src/Serilog.Extensions.Logging/Extensions/Logging/LevelMapping.cs index 1fd5cd1..44b543d 100644 --- a/src/Serilog.Extensions.Logging/Extensions/Logging/LevelMapping.cs +++ b/src/Serilog.Extensions.Logging/Extensions/Logging/LevelMapping.cs @@ -1,4 +1,18 @@ -using Microsoft.Extensions.Logging; +// Copyright 2019 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 Microsoft.Extensions.Logging; using Serilog.Events; namespace Serilog.Extensions.Logging diff --git a/src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollectionSink.cs b/src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollectionSink.cs index dca9826..ba3a93b 100644 --- a/src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollectionSink.cs +++ b/src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollectionSink.cs @@ -13,12 +13,8 @@ // limitations under the License. using System; -using System.Linq; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Internal; using Serilog.Core; using Serilog.Events; -using Serilog.Parsing; namespace Serilog.Extensions.Logging { @@ -42,29 +38,17 @@ sourceContextProperty is ScalarValue sourceContextValue && categoryName = sourceContext; } - // Allocates like mad, but first make it work, then make it work fast ;-) - var flv = new FormattedLogValues( - logEvent.MessageTemplate.Text, - logEvent.MessageTemplate.Tokens - .OfType() - .Select(p => - { - if (!logEvent.Properties.TryGetValue(p.PropertyName, out var value)) - return null; - if (value is ScalarValue sv) - return sv.Value; - return value; - }) - .ToArray()); + var level = LevelMapping.ToExtensionsLevel(logEvent.Level); + var slv = new SerilogLogValues(logEvent.MessageTemplate, logEvent.Properties); foreach (var provider in _providers.Providers) { var logger = provider.CreateLogger(categoryName); logger.Log( - LevelMapping.ToExtensionsLevel(logEvent.Level), - default(EventId), - flv, + level, + default, + slv, logEvent.Exception, (s, e) => s.ToString()); } diff --git a/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLogValues.cs b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLogValues.cs new file mode 100644 index 0000000..c6f8058 --- /dev/null +++ b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLogValues.cs @@ -0,0 +1,62 @@ +// Copyright 2019 Serilog Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Serilog.Events; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Serilog.Extensions.Logging +{ + readonly struct SerilogLogValues : IReadOnlyList> + { + // Note, this struct is only used in a very limited context internally, so we ignore + // the possibility of fields being null via the default struct initialization. + + private readonly MessageTemplate _messageTemplate; + private readonly IReadOnlyDictionary _properties; + private readonly KeyValuePair[] _values; + + public SerilogLogValues(MessageTemplate messageTemplate, IReadOnlyDictionary properties) + { + _messageTemplate = messageTemplate ?? throw new ArgumentNullException(nameof(messageTemplate)); + + // The dictionary is needed for rendering through the message template + _properties = properties ?? throw new ArgumentNullException(nameof(properties)); + + // The array is needed because the IReadOnlyList interface expects indexed access + _values = new KeyValuePair[_properties.Count + 1]; + var i = 0; + foreach (var p in properties) + { + _values[i] = new KeyValuePair(p.Key, (p.Value is ScalarValue sv) ? sv.Value : p.Value); + ++i; + } + _values[i] = new KeyValuePair("{OriginalFormat}", _messageTemplate.Text); + } + + public KeyValuePair this[int index] + { + get => _values[index]; + } + + public int Count => _properties.Count + 1; + + public IEnumerator> GetEnumerator() => ((IEnumerable>)_values).GetEnumerator(); + + public override string ToString() => _messageTemplate.Render(_properties); + + IEnumerator IEnumerable.GetEnumerator() => _values.GetEnumerator(); + } +} diff --git a/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj b/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj index ba448ff..565e550 100644 --- a/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj +++ b/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj @@ -2,9 +2,9 @@ Low-level Serilog provider for Microsoft.Extensions.Logging - 2.0.5 + 3.0.0 Microsoft;Serilog Contributors - net45;net46;net461;netstandard1.3;netstandard2.0 + netstandard2.0 true true Serilog.Extensions.Logging @@ -24,28 +24,9 @@ - - - - - - - - - $(DefineConstants);ASYNCLOCAL - - - - $(DefineConstants);ASYNCLOCAL;LOGGING_BUILDER - - - - $(DefineConstants);ASYNCLOCAL - - $(DefineConstants);ASYNCLOCAL;LOGGING_BUILDER diff --git a/test/Serilog.Extensions.Logging.Tests/Serilog.Extensions.Logging.Tests.csproj b/test/Serilog.Extensions.Logging.Tests/Serilog.Extensions.Logging.Tests.csproj index 54eee14..7f8a2ff 100644 --- a/test/Serilog.Extensions.Logging.Tests/Serilog.Extensions.Logging.Tests.csproj +++ b/test/Serilog.Extensions.Logging.Tests/Serilog.Extensions.Logging.Tests.csproj @@ -1,15 +1,13 @@  - netcoreapp1.1;netcoreapp2.0;net46;net461 + netcoreapp2.0;net472 Serilog.Extensions.Logging.Tests ../../assets/Serilog.snk true true Serilog.Extensions.Logging.Tests true - $(PackageTargetFallback);dnxcore50;portable-net45+win8 - 1.0.4 @@ -22,9 +20,4 @@ - - - - - diff --git a/test/Serilog.Extensions.Logging.Tests/SerilogLogValuesTests.cs b/test/Serilog.Extensions.Logging.Tests/SerilogLogValuesTests.cs new file mode 100644 index 0000000..c810ec3 --- /dev/null +++ b/test/Serilog.Extensions.Logging.Tests/SerilogLogValuesTests.cs @@ -0,0 +1,53 @@ +using Serilog.Events; +using Serilog.Parsing; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace Serilog.Extensions.Logging.Tests +{ + public class SerilogLogValuesTests + { + [Fact] + public void OriginalFormatIsExposed() + { + const string format = "Hello, {Name}!"; + var mt = new MessageTemplateParser().Parse(format); + var lv = new SerilogLogValues(mt, new Dictionary()); + var kvp = lv.Single(); + Assert.Equal("{OriginalFormat}", kvp.Key); + Assert.Equal(format, kvp.Value); + } + + [Fact] + public void ScalarPropertiesAreSimplfied() + { + const string name = "Scalar"; + var scalar = 15; + var lv = new SerilogLogValues(MessageTemplate.Empty, new Dictionary { [name] = new ScalarValue(scalar) }); + var kvp = lv.Single(p => p.Key == name); + var sv = Assert.IsType(kvp.Value); + Assert.Equal(scalar, sv); + } + + [Fact] + public void NonscalarPropertiesAreWrapped() + { + const string name = "Sequence"; + var seq = new SequenceValue(Enumerable.Empty()); + var lv = new SerilogLogValues(MessageTemplate.Empty, new Dictionary { [name] = seq }); + var kvp = lv.Single(p => p.Key == name); + var sv = Assert.IsType(kvp.Value); + Assert.Equal(seq, sv); + } + + [Fact] + public void MessageTemplatesAreRendered() + { + const string format = "Hello, {Name}!"; + var mt = new MessageTemplateParser().Parse(format); + var lv = new SerilogLogValues(mt, new Dictionary { ["Name"] = new ScalarValue("World") }); + Assert.Equal("Hello, \"World\"!", lv.ToString()); + } + } +} From 1b3b8853cd6d2d04c41852db02c8d3f945b50cea Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 16 May 2019 15:05:16 +1000 Subject: [PATCH 13/22] Typos/Cleanup --- serilog-extensions-logging.sln.DotSettings | 7 +++++++ .../SerilogLogValuesTests.cs | 2 +- .../Serilog.Extensions.Logging.Tests/SerilogLoggerTests.cs | 4 ++-- .../Support/DisposeTrackingLogger.cs | 2 +- .../Support/SerilogSink.cs | 2 +- 5 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 serilog-extensions-logging.sln.DotSettings diff --git a/serilog-extensions-logging.sln.DotSettings b/serilog-extensions-logging.sln.DotSettings new file mode 100644 index 0000000..c6bac2f --- /dev/null +++ b/serilog-extensions-logging.sln.DotSettings @@ -0,0 +1,7 @@ + + True + True + True + True + True + True \ No newline at end of file diff --git a/test/Serilog.Extensions.Logging.Tests/SerilogLogValuesTests.cs b/test/Serilog.Extensions.Logging.Tests/SerilogLogValuesTests.cs index c810ec3..d5a777e 100644 --- a/test/Serilog.Extensions.Logging.Tests/SerilogLogValuesTests.cs +++ b/test/Serilog.Extensions.Logging.Tests/SerilogLogValuesTests.cs @@ -20,7 +20,7 @@ public void OriginalFormatIsExposed() } [Fact] - public void ScalarPropertiesAreSimplfied() + public void ScalarPropertiesAreSimplified() { const string name = "Scalar"; var scalar = 15; diff --git a/test/Serilog.Extensions.Logging.Tests/SerilogLoggerTests.cs b/test/Serilog.Extensions.Logging.Tests/SerilogLoggerTests.cs index e57d4a8..a83ca0c 100644 --- a/test/Serilog.Extensions.Logging.Tests/SerilogLoggerTests.cs +++ b/test/Serilog.Extensions.Logging.Tests/SerilogLoggerTests.cs @@ -9,10 +9,10 @@ using System.IO; using System.Linq; using Serilog.Debugging; -using Serilog.Framework.Logging.Tests.Support; +using Serilog.Extensions.Logging.Tests.Support; using Xunit; -namespace Serilog.Extensions.Logging.Test +namespace Serilog.Extensions.Logging.Tests { public class SerilogLoggerTest { diff --git a/test/Serilog.Extensions.Logging.Tests/Support/DisposeTrackingLogger.cs b/test/Serilog.Extensions.Logging.Tests/Support/DisposeTrackingLogger.cs index a967fef..d833c0c 100644 --- a/test/Serilog.Extensions.Logging.Tests/Support/DisposeTrackingLogger.cs +++ b/test/Serilog.Extensions.Logging.Tests/Support/DisposeTrackingLogger.cs @@ -3,7 +3,7 @@ using Serilog.Core; using Serilog.Events; -namespace Serilog.Framework.Logging.Tests.Support +namespace Serilog.Extensions.Logging.Tests.Support { public class DisposeTrackingLogger : ILogger, IDisposable { diff --git a/test/Serilog.Extensions.Logging.Tests/Support/SerilogSink.cs b/test/Serilog.Extensions.Logging.Tests/Support/SerilogSink.cs index a0f6384..cd7d5d7 100644 --- a/test/Serilog.Extensions.Logging.Tests/Support/SerilogSink.cs +++ b/test/Serilog.Extensions.Logging.Tests/Support/SerilogSink.cs @@ -5,7 +5,7 @@ using Serilog.Core; using Serilog.Events; -namespace Serilog.Extensions.Logging.Test +namespace Serilog.Extensions.Logging.Tests.Support { public class SerilogSink : ILogEventSink { From e11cf7e946030e863162d65a98821e35061965e9 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 16 May 2019 15:09:25 +1000 Subject: [PATCH 14/22] Use an SDK version present on AppVeyor :-) --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 7ed2ef7..6b64107 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "2.2.202" + "version": "2.2.103" } } From c95077076bf60f4e35fea88e9522b0a14dc99467 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 16 May 2019 15:22:41 +1000 Subject: [PATCH 15/22] LangVersion --- src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj b/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj index 565e550..eeaa59c 100644 --- a/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj +++ b/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj @@ -20,6 +20,7 @@ git false Serilog + 7.3 From d2155ce0124e79f9f7c5f77f89e60b097125f3bc Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 16 May 2019 15:29:32 +1000 Subject: [PATCH 16/22] Add usual level control params to WriteTo.Providers() --- .../LoggerSinkConfigurationExtensions.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Serilog.Extensions.Logging/LoggerSinkConfigurationExtensions.cs b/src/Serilog.Extensions.Logging/LoggerSinkConfigurationExtensions.cs index 8524828..85064db 100644 --- a/src/Serilog.Extensions.Logging/LoggerSinkConfigurationExtensions.cs +++ b/src/Serilog.Extensions.Logging/LoggerSinkConfigurationExtensions.cs @@ -14,6 +14,8 @@ using System; using Serilog.Configuration; +using Serilog.Core; +using Serilog.Events; using Serilog.Extensions.Logging; namespace Serilog @@ -28,12 +30,20 @@ public static class LoggerSinkConfigurationExtensions /// /// The `WriteTo` object. /// A to write events to. + /// 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. /// A to allow method chaining. - public static LoggerConfiguration Providers(this LoggerSinkConfiguration configuration, LoggerProviderCollection providers) + public static LoggerConfiguration Providers( + this LoggerSinkConfiguration configuration, + LoggerProviderCollection providers, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) { if (configuration == null) throw new ArgumentNullException(nameof(configuration)); if (providers == null) throw new ArgumentNullException(nameof(providers)); - return configuration.Sink(new LoggerProviderCollectionSink(providers)); + return configuration.Sink(new LoggerProviderCollectionSink(providers), restrictedToMinimumLevel, levelSwitch); } } } From a9923aec3ed051b7450cd7b1b45c68b17a9519e9 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Tue, 21 May 2019 17:42:34 +1000 Subject: [PATCH 17/22] Remove some unused build-time constants --- .../Extensions/Logging/SerilogLoggerProvider.cs | 15 +++------------ .../Serilog.Extensions.Logging.csproj | 4 ---- .../SerilogLoggingBuilderExtensions.cs | 4 ---- 3 files changed, 3 insertions(+), 20 deletions(-) diff --git a/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs index 3590071..76b32cc 100644 --- a/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs +++ b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs @@ -2,12 +2,12 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Threading; using Microsoft.Extensions.Logging; using Serilog.Core; using Serilog.Events; using FrameworkLogger = Microsoft.Extensions.Logging.ILogger; using System.Collections.Generic; +using System.Threading; using Serilog.Context; namespace Serilog.Extensions.Logging @@ -15,9 +15,7 @@ namespace Serilog.Extensions.Logging /// /// An that pipes events through Serilog. /// -#if LOGGING_BUILDER [ProviderAlias("Serilog")] -#endif public class SerilogLoggerProvider : ILoggerProvider, ILogEventEnricher { internal const string OriginalFormatPropertyName = "{OriginalFormat}"; @@ -92,15 +90,8 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) internal SerilogLoggerScope CurrentScope { - get - { - return _value.Value; - } - set - { - _value.Value = value; - } - } + get => _value.Value; + set => _value.Value = value; /// public void Dispose() diff --git a/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj b/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj index 40b882d..479cb20 100644 --- a/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj +++ b/src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj @@ -28,8 +28,4 @@ - - $(DefineConstants);ASYNCLOCAL;LOGGING_BUILDER - - diff --git a/src/Serilog.Extensions.Logging/SerilogLoggingBuilderExtensions.cs b/src/Serilog.Extensions.Logging/SerilogLoggingBuilderExtensions.cs index d7a3f5f..27bb6dd 100644 --- a/src/Serilog.Extensions.Logging/SerilogLoggingBuilderExtensions.cs +++ b/src/Serilog.Extensions.Logging/SerilogLoggingBuilderExtensions.cs @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#if LOGGING_BUILDER - using System; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -54,5 +52,3 @@ public static ILoggingBuilder AddSerilog(this ILoggingBuilder builder, ILogger l } } } - -#endif // LOGGING_BUILDER From 0885a2a9d126c6c36f6bdb4338847a207deced12 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Tue, 21 May 2019 19:33:19 +1000 Subject: [PATCH 18/22] Keyboard fumble --- .../Extensions/Logging/SerilogLoggerProvider.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs index 76b32cc..f4daf8a 100644 --- a/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs +++ b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs @@ -92,6 +92,7 @@ internal SerilogLoggerScope CurrentScope { get => _value.Value; set => _value.Value = value; + } /// public void Dispose() From 3f73c406dac24bd9416275daec6eb8bf5859f003 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Tue, 21 May 2019 20:13:21 +1000 Subject: [PATCH 19/22] Make the level mapping routines public --- .../{LevelMapping.cs => LevelConvert.cs} | 23 ++++++++++++++++--- .../Logging/LoggerProviderCollectionSink.cs | 2 +- .../Extensions/Logging/SerilogLogger.cs | 4 ++-- 3 files changed, 23 insertions(+), 6 deletions(-) rename src/Serilog.Extensions.Logging/Extensions/Logging/{LevelMapping.cs => LevelConvert.cs} (64%) diff --git a/src/Serilog.Extensions.Logging/Extensions/Logging/LevelMapping.cs b/src/Serilog.Extensions.Logging/Extensions/Logging/LevelConvert.cs similarity index 64% rename from src/Serilog.Extensions.Logging/Extensions/Logging/LevelMapping.cs rename to src/Serilog.Extensions.Logging/Extensions/Logging/LevelConvert.cs index 44b543d..58354e0 100644 --- a/src/Serilog.Extensions.Logging/Extensions/Logging/LevelMapping.cs +++ b/src/Serilog.Extensions.Logging/Extensions/Logging/LevelConvert.cs @@ -15,14 +15,28 @@ using Microsoft.Extensions.Logging; using Serilog.Events; +// ReSharper disable RedundantCaseLabel + namespace Serilog.Extensions.Logging { - static class LevelMapping + /// + /// Converts between Serilog and Microsoft.Extensions.Logging level enum values. + /// + public static class LevelConvert { + /// + /// Convert to the equivalent Serilog . + /// + /// A Microsoft.Extensions.Logging . + /// The Serilog equivalent of . + /// The value has no Serilog equivalent. It is mapped to + /// as the closest approximation, but this has entirely + /// different semantics. public static LogEventLevel ToSerilogLevel(LogLevel logLevel) { switch (logLevel) { + case LogLevel.None: case LogLevel.Critical: return LogEventLevel.Fatal; case LogLevel.Error: @@ -33,13 +47,17 @@ public static LogEventLevel ToSerilogLevel(LogLevel logLevel) return LogEventLevel.Information; case LogLevel.Debug: return LogEventLevel.Debug; - // ReSharper disable once RedundantCaseLabel case LogLevel.Trace: default: return LogEventLevel.Verbose; } } + /// + /// Convert to the equivalent Microsoft.Extensions.Logging . + /// + /// A Serilog . + /// The Microsoft.Extensions.Logging equivalent of . public static LogLevel ToExtensionsLevel(LogEventLevel logEventLevel) { switch (logEventLevel) @@ -54,7 +72,6 @@ public static LogLevel ToExtensionsLevel(LogEventLevel logEventLevel) return LogLevel.Information; case LogEventLevel.Debug: return LogLevel.Debug; - // ReSharper disable once RedundantCaseLabel case LogEventLevel.Verbose: default: return LogLevel.Trace; diff --git a/src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollectionSink.cs b/src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollectionSink.cs index ba3a93b..94838ea 100644 --- a/src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollectionSink.cs +++ b/src/Serilog.Extensions.Logging/Extensions/Logging/LoggerProviderCollectionSink.cs @@ -38,7 +38,7 @@ sourceContextProperty is ScalarValue sourceContextValue && categoryName = sourceContext; } - var level = LevelMapping.ToExtensionsLevel(logEvent.Level); + var level = LevelConvert.ToExtensionsLevel(logEvent.Level); var slv = new SerilogLogValues(logEvent.MessageTemplate, logEvent.Properties); foreach (var provider in _providers.Providers) diff --git a/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLogger.cs b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLogger.cs index e81f79e..6f91fa1 100644 --- a/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLogger.cs +++ b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLogger.cs @@ -39,7 +39,7 @@ public SerilogLogger( public bool IsEnabled(LogLevel logLevel) { - return _logger.IsEnabled(LevelMapping.ToSerilogLevel(logLevel)); + return _logger.IsEnabled(LevelConvert.ToSerilogLevel(logLevel)); } public IDisposable BeginScope(TState state) @@ -49,7 +49,7 @@ public IDisposable BeginScope(TState state) public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { - var level = LevelMapping.ToSerilogLevel(logLevel); + var level = LevelConvert.ToSerilogLevel(logLevel); if (!_logger.IsEnabled(level)) { return; From 95ab1c19c8fca300493f4bd88d216435e8d90682 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Mon, 19 Aug 2019 16:52:30 +1000 Subject: [PATCH 20/22] Update to use VS 2019 final image --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 6b98e44..dc593a6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,6 @@ version: '{build}' skip_tags: true -image: Visual Studio 2019 Preview +image: Visual Studio 2019 configuration: Release install: - ps: mkdir -Force ".\build\" | Out-Null From 8301b4e687d9565a9073f8e51b41e81791a43a11 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Tue, 21 May 2019 21:40:17 +1000 Subject: [PATCH 21/22] Cache low-numbered numeric event ids to reduce allocations --- serilog-extensions-logging.sln.DotSettings | 6 +- .../Extensions/Logging/SerilogLogger.cs | 40 ++--- .../SerilogLoggerTests.cs | 138 +++++++----------- 3 files changed, 77 insertions(+), 107 deletions(-) diff --git a/serilog-extensions-logging.sln.DotSettings b/serilog-extensions-logging.sln.DotSettings index c6bac2f..4009815 100644 --- a/serilog-extensions-logging.sln.DotSettings +++ b/serilog-extensions-logging.sln.DotSettings @@ -1,7 +1,11 @@  True + True True + True True True + True True - True \ No newline at end of file + True + True \ No newline at end of file diff --git a/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLogger.cs b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLogger.cs index 6f91fa1..89b9f93 100644 --- a/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLogger.cs +++ b/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLogger.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; +using System.Linq; using Serilog.Core; using Serilog.Events; using FrameworkLogger = Microsoft.Extensions.Logging.ILogger; @@ -17,15 +18,19 @@ class SerilogLogger : FrameworkLogger readonly SerilogLoggerProvider _provider; readonly ILogger _logger; - static readonly MessageTemplateParser _messageTemplateParser = new MessageTemplateParser(); + static readonly MessageTemplateParser MessageTemplateParser = new MessageTemplateParser(); + + // It's rare to see large event ids, as they are category-specific + static readonly LogEventProperty[] LowEventIdValues = Enumerable.Range(0, 48) + .Select(n => new LogEventProperty("Id", new ScalarValue(n))) + .ToArray(); public SerilogLogger( SerilogLoggerProvider provider, ILogger logger = null, string name = null) { - if (provider == null) throw new ArgumentNullException(nameof(provider)); - _provider = provider; + _provider = provider ?? throw new ArgumentNullException(nameof(provider)); _logger = logger; // If a logger was passed, the provider has already added itself as an enricher @@ -60,25 +65,22 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except var properties = new List(); - var structure = state as IEnumerable>; - if (structure != null) + if (state is IEnumerable> structure) { foreach (var property in structure) { - if (property.Key == SerilogLoggerProvider.OriginalFormatPropertyName && property.Value is string) + if (property.Key == SerilogLoggerProvider.OriginalFormatPropertyName && property.Value is string value) { - messageTemplate = (string)property.Value; + messageTemplate = value; } else if (property.Key.StartsWith("@")) { - LogEventProperty destructured; - if (logger.BindProperty(property.Key.Substring(1), property.Value, true, out destructured)) + if (logger.BindProperty(property.Key.Substring(1), property.Value, true, out var destructured)) properties.Add(destructured); } else { - LogEventProperty bound; - if (logger.BindProperty(property.Key, property.Value, false, out bound)) + if (logger.BindProperty(property.Key, property.Value, false, out var bound)) properties.Add(bound); } } @@ -89,8 +91,7 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except if (messageTemplate == null && !stateTypeInfo.IsGenericType) { messageTemplate = "{" + stateType.Name + ":l}"; - LogEventProperty stateTypeProperty; - if (logger.BindProperty(stateType.Name, AsLoggableValue(state, formatter), false, out stateTypeProperty)) + if (logger.BindProperty(stateType.Name, AsLoggableValue(state, formatter), false, out var stateTypeProperty)) properties.Add(stateTypeProperty); } } @@ -111,8 +112,7 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except if (propertyName != null) { - LogEventProperty property; - if (logger.BindProperty(propertyName, AsLoggableValue(state, formatter), false, out property)) + if (logger.BindProperty(propertyName, AsLoggableValue(state, formatter), false, out var property)) properties.Add(property); } } @@ -120,7 +120,7 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except if (eventId.Id != 0 || eventId.Name != null) properties.Add(CreateEventIdProperty(eventId)); - var parsedTemplate = _messageTemplateParser.Parse(messageTemplate ?? ""); + var parsedTemplate = MessageTemplateParser.Parse(messageTemplate ?? ""); var evt = new LogEvent(DateTimeOffset.Now, level, exception, parsedTemplate, properties); logger.Write(evt); } @@ -133,13 +133,17 @@ static object AsLoggableValue(TState state, Func(2); if (eventId.Id != 0) { - properties.Add(new LogEventProperty("Id", new ScalarValue(eventId.Id))); + if (eventId.Id < LowEventIdValues.Length) + // Avoid some allocations + properties.Add(LowEventIdValues[eventId.Id]); + else + properties.Add(new LogEventProperty("Id", new ScalarValue(eventId.Id))); } if (eventId.Name != null) diff --git a/test/Serilog.Extensions.Logging.Tests/SerilogLoggerTests.cs b/test/Serilog.Extensions.Logging.Tests/SerilogLoggerTests.cs index a83ca0c..b778a44 100644 --- a/test/Serilog.Extensions.Logging.Tests/SerilogLoggerTests.cs +++ b/test/Serilog.Extensions.Logging.Tests/SerilogLoggerTests.cs @@ -16,56 +16,28 @@ namespace Serilog.Extensions.Logging.Tests { public class SerilogLoggerTest { - private const string Name = "test"; - private const string TestMessage = "This is a test"; + const string Name = "test"; + const string TestMessage = "This is a test"; - private Tuple SetUp(LogLevel logLevel) + static Tuple SetUp(LogLevel logLevel) { var sink = new SerilogSink(); - var config = new LoggerConfiguration() - .WriteTo.Sink(sink); + var serilogLogger = new LoggerConfiguration() + .WriteTo.Sink(sink) + .MinimumLevel.Is(LevelMapping.ToSerilogLevel(logLevel)) + .CreateLogger(); - SetMinLevel(config, logLevel); - - var provider = new SerilogLoggerProvider(config.CreateLogger()); + var provider = new SerilogLoggerProvider(serilogLogger); var logger = (SerilogLogger)provider.CreateLogger(Name); return new Tuple(logger, sink); } - private void SetMinLevel(LoggerConfiguration serilog, LogLevel logLevel) - { - serilog.MinimumLevel.Is(MapLevel(logLevel)); - } - - private LogEventLevel MapLevel(LogLevel logLevel) - { - switch (logLevel) - { - case LogLevel.Trace: - return LogEventLevel.Verbose; - case LogLevel.Debug: - return LogEventLevel.Debug; - case LogLevel.Information: - return LogEventLevel.Information; - case LogLevel.Warning: - return LogEventLevel.Warning; - case LogLevel.Error: - return LogEventLevel.Error; - case LogLevel.Critical: - return LogEventLevel.Fatal; - default: - return LogEventLevel.Verbose; - } - } - [Fact] public void LogsWhenNullFilterGiven() { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + var (logger, sink) = SetUp(LogLevel.Trace); logger.Log(LogLevel.Information, 0, TestMessage, null, null); @@ -75,9 +47,7 @@ public void LogsWhenNullFilterGiven() [Fact] public void LogsCorrectLevel() { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + var (logger, sink) = SetUp(LogLevel.Trace); logger.Log(LogLevel.Trace, 0, TestMessage, null, null); logger.Log(LogLevel.Debug, 0, TestMessage, null, null); @@ -134,9 +104,7 @@ public void LogsCorrectLevel() [InlineData(LogLevel.Critical, LogLevel.Critical, 1)] public void LogsWhenEnabled(LogLevel minLevel, LogLevel logLevel, int expected) { - var t = SetUp(minLevel); - var logger = t.Item1; - var sink = t.Item2; + var (logger, sink) = SetUp(minLevel); logger.Log(logLevel, 0, TestMessage, null, null); @@ -146,9 +114,7 @@ public void LogsWhenEnabled(LogLevel minLevel, LogLevel logLevel, int expected) [Fact] public void LogsCorrectMessage() { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + var (logger, sink) = SetUp(LogLevel.Trace); logger.Log(LogLevel.Information, 0, null, null, null); logger.Log(LogLevel.Information, 0, TestMessage, null, null); @@ -171,9 +137,7 @@ public void LogsCorrectMessage() [Fact] public void CarriesException() { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + var (logger, sink) = SetUp(LogLevel.Trace); var exception = new Exception(); @@ -186,9 +150,7 @@ public void CarriesException() [Fact] public void SingleScopeProperty() { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + var (logger, sink) = SetUp(LogLevel.Trace); using (logger.BeginScope(new FoodScope("pizza"))) { @@ -203,9 +165,7 @@ public void SingleScopeProperty() [Fact] public void NestedScopeSameProperty() { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + var (logger, sink) = SetUp(LogLevel.Trace); using (logger.BeginScope(new FoodScope("avocado"))) { @@ -224,9 +184,7 @@ public void NestedScopeSameProperty() [Fact] public void NestedScopesDifferentProperties() { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + var (logger, sink) = SetUp(LogLevel.Trace); using (logger.BeginScope(new FoodScope("spaghetti"))) { @@ -249,9 +207,7 @@ public void CarriesMessageTemplateProperties() var selfLog = new StringWriter(); SelfLog.Enable(selfLog); - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + var (logger, sink) = SetUp(LogLevel.Trace); logger.LogInformation("Hello, {Recipient}", "World"); @@ -266,11 +222,9 @@ public void CarriesMessageTemplateProperties() [Fact] public void CarriesEventIdIfNonzero() { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + var (logger, sink) = SetUp(LogLevel.Trace); - int expected = 42; + const int expected = 42; logger.Log(LogLevel.Information, expected, "Test", null, null); @@ -302,9 +256,7 @@ public void WhenDisposeIsTrueProvidedLoggerIsDisposed() [Fact] public void BeginScopeDestructuresObjectsWhenDestructurerIsUsedInMessageTemplate() { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + var (logger, sink) = SetUp(LogLevel.Trace); using (logger.BeginScope("{@Person}", new Person { FirstName = "John", LastName = "Smith" })) { @@ -324,9 +276,7 @@ public void BeginScopeDestructuresObjectsWhenDestructurerIsUsedInMessageTemplate [Fact] public void BeginScopeDestructuresObjectsWhenDestructurerIsUsedInDictionary() { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + var (logger, sink) = SetUp(LogLevel.Trace); using (logger.BeginScope(new Dictionary {{ "@Person", new Person { FirstName = "John", LastName = "Smith" }}})) { @@ -346,9 +296,7 @@ public void BeginScopeDestructuresObjectsWhenDestructurerIsUsedInDictionary() [Fact] public void BeginScopeDoesNotModifyKeyWhenDestructurerIsNotUsedInMessageTemplate() { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + var (logger, sink) = SetUp(LogLevel.Trace); using (logger.BeginScope("{FirstName}", "John")) { @@ -362,9 +310,7 @@ public void BeginScopeDoesNotModifyKeyWhenDestructurerIsNotUsedInMessageTemplate [Fact] public void BeginScopeDoesNotModifyKeyWhenDestructurerIsNotUsedInDictionary() { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + var (logger, sink) = SetUp(LogLevel.Trace); using (logger.BeginScope(new Dictionary { { "FirstName", "John"}})) { @@ -378,9 +324,7 @@ public void BeginScopeDoesNotModifyKeyWhenDestructurerIsNotUsedInDictionary() [Fact] public void NamedScopesAreCaptured() { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + var (logger, sink) = SetUp(LogLevel.Trace); using (logger.BeginScope("Outer")) using (logger.BeginScope("Inner")) @@ -390,16 +334,15 @@ public void NamedScopesAreCaptured() Assert.Equal(1, sink.Writes.Count); - LogEventPropertyValue scopeValue; - Assert.True(sink.Writes[0].Properties.TryGetValue(SerilogLoggerProvider.ScopePropertyName, out scopeValue)); - - var items = (scopeValue as SequenceValue)?.Elements.Select(e => ((ScalarValue)e).Value).Cast().ToArray(); + Assert.True(sink.Writes[0].Properties.TryGetValue(SerilogLoggerProvider.ScopePropertyName, out var scopeValue)); + var sequence = Assert.IsType(scopeValue); + var items = sequence.Elements.Select(e => Assert.IsType(e).Value).Cast().ToArray(); Assert.Equal(2, items.Length); Assert.Equal("Outer", items[0]); Assert.Equal("Inner", items[1]); } - private class FoodScope : IEnumerable> + class FoodScope : IEnumerable> { readonly string _name; @@ -419,7 +362,7 @@ IEnumerator IEnumerable.GetEnumerator() } } - private class LuckyScope : IEnumerable> + class LuckyScope : IEnumerable> { readonly int _luckyNumber; @@ -439,10 +382,29 @@ IEnumerator IEnumerable.GetEnumerator() } } - private class Person + class Person { + // ReSharper disable once UnusedAutoPropertyAccessor.Local public string FirstName { get; set; } + + // ReSharper disable once UnusedAutoPropertyAccessor.Local public string LastName { get; set; } } + + [Theory] + [InlineData(1)] + [InlineData(10)] + [InlineData(48)] + [InlineData(100)] + public void LowAndHighNumberedEventIdsAreMapped(int id) + { + var orig = new EventId(id, "test"); + var mapped = SerilogLogger.CreateEventIdProperty(orig); + var value = Assert.IsType(mapped.Value); + Assert.Equal(2, value.Properties.Count); + var idValue = value.Properties.Single(p => p.Name == "Id").Value; + var scalar = Assert.IsType(idValue); + Assert.Equal(id, scalar.Value); + } } -} \ No newline at end of file +} From 40a2a2d26df9d98a92be3289f38ad9bf59c1ce72 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Tue, 20 Aug 2019 13:00:31 +1000 Subject: [PATCH 22/22] Add some basic benchmarks --- serilog-extensions-logging.sln | 11 +- .../LogEventConstructionBenchmark.cs | 103 ++++++++++++++++++ ...rilog.Extensions.Logging.Benchmarks.csproj | 26 +++++ .../Support/CapturingSink.cs | 36 ++++++ .../SerilogLoggerTests.cs | 2 +- 5 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 test/Serilog.Extensions.Logging.Benchmarks/LogEventConstructionBenchmark.cs create mode 100644 test/Serilog.Extensions.Logging.Benchmarks/Serilog.Extensions.Logging.Benchmarks.csproj create mode 100644 test/Serilog.Extensions.Logging.Benchmarks/Support/CapturingSink.cs diff --git a/serilog-extensions-logging.sln b/serilog-extensions-logging.sln index 80abb55..c53414c 100644 --- a/serilog-extensions-logging.sln +++ b/serilog-extensions-logging.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.10 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29209.62 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A1893BD1-333D-4DFE-A0F0-DDBB2FE526E0}" EndProject @@ -24,6 +24,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{9C21B9 assets\Serilog.snk = assets\Serilog.snk EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Extensions.Logging.Benchmarks", "test\Serilog.Extensions.Logging.Benchmarks\Serilog.Extensions.Logging.Benchmarks.csproj", "{6D5986FF-EECD-4E75-8BC6-A5F78AB549B2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -42,6 +44,10 @@ Global {65357FBC-9BC4-466D-B621-1C3A19BC2A78}.Debug|Any CPU.Build.0 = Debug|Any CPU {65357FBC-9BC4-466D-B621-1C3A19BC2A78}.Release|Any CPU.ActiveCfg = Release|Any CPU {65357FBC-9BC4-466D-B621-1C3A19BC2A78}.Release|Any CPU.Build.0 = Release|Any CPU + {6D5986FF-EECD-4E75-8BC6-A5F78AB549B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6D5986FF-EECD-4E75-8BC6-A5F78AB549B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D5986FF-EECD-4E75-8BC6-A5F78AB549B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6D5986FF-EECD-4E75-8BC6-A5F78AB549B2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -50,6 +56,7 @@ Global {903CD13A-D54B-4CEC-A55F-E22AE3D93B3B} = {A1893BD1-333D-4DFE-A0F0-DDBB2FE526E0} {37EADF84-5E41-4224-A194-1E3299DCD0B8} = {E30F638E-BBBE-4AD1-93CE-48CC69CFEFE1} {65357FBC-9BC4-466D-B621-1C3A19BC2A78} = {F2407211-6043-439C-8E06-3641634332E7} + {6D5986FF-EECD-4E75-8BC6-A5F78AB549B2} = {E30F638E-BBBE-4AD1-93CE-48CC69CFEFE1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {811E61C5-3871-4633-AFAE-B35B619C8A10} diff --git a/test/Serilog.Extensions.Logging.Benchmarks/LogEventConstructionBenchmark.cs b/test/Serilog.Extensions.Logging.Benchmarks/LogEventConstructionBenchmark.cs new file mode 100644 index 0000000..51cae2e --- /dev/null +++ b/test/Serilog.Extensions.Logging.Benchmarks/LogEventConstructionBenchmark.cs @@ -0,0 +1,103 @@ +// Copyright 2019 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 BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; +using Microsoft.Extensions.Logging; +using IMelLogger = Microsoft.Extensions.Logging.ILogger; +using Serilog.Events; +using Serilog.Extensions.Logging.Benchmarks.Support; +using Xunit; + +namespace Serilog.Extensions.Logging.Benchmarks +{ + [MemoryDiagnoser] + public class LogEventConstructionBenchmark + { + readonly IMelLogger _melLogger; + readonly ILogger _serilogContextualLogger; + readonly CapturingSink _sink; + const int LowId = 10, HighId = 101; + const string Template = "This is an event"; + + public LogEventConstructionBenchmark() + { + _sink = new CapturingSink(); + var underlyingLogger = new LoggerConfiguration().WriteTo.Sink(_sink).CreateLogger(); + _serilogContextualLogger = underlyingLogger.ForContext(); + _melLogger = new SerilogLoggerProvider(underlyingLogger).CreateLogger(GetType().FullName); + } + + static void VerifyEventId(LogEvent evt, int? expectedId) + { + if (evt == null) throw new ArgumentNullException(nameof(evt)); + if (expectedId == null) + { + Assert.False(evt.Properties.TryGetValue("EventId", out _)); + } + else + { + Assert.True(evt.Properties.TryGetValue("EventId", out var eventIdValue)); + var structure = Assert.IsType(eventIdValue); + var idValue = Assert.Single(structure.Properties, p => p.Name == "Id")?.Value; + var scalar = Assert.IsType(idValue); + Assert.Equal(expectedId.Value, scalar.Value); + } + } + + [Fact] + public void Verify() + { + VerifyEventId(Native(), null); + VerifyEventId(NoId(), null); + VerifyEventId(LowNumbered(), LowId); + VerifyEventId(HighNumbered(), HighId); + } + + [Fact] + public void Benchmark() + { + BenchmarkRunner.Run(); + } + + [Benchmark(Baseline = true)] + public LogEvent Native() + { + _serilogContextualLogger.Information(Template); + return _sink.Collect(); + } + + [Benchmark] + public LogEvent NoId() + { + _melLogger.LogInformation(Template); + return _sink.Collect(); + } + + [Benchmark] + public LogEvent LowNumbered() + { + _melLogger.LogInformation(LowId, Template); + return _sink.Collect(); + } + + [Benchmark] + public LogEvent HighNumbered() + { + _melLogger.LogInformation(HighId, Template); + return _sink.Collect(); + } + } +} diff --git a/test/Serilog.Extensions.Logging.Benchmarks/Serilog.Extensions.Logging.Benchmarks.csproj b/test/Serilog.Extensions.Logging.Benchmarks/Serilog.Extensions.Logging.Benchmarks.csproj new file mode 100644 index 0000000..7840f21 --- /dev/null +++ b/test/Serilog.Extensions.Logging.Benchmarks/Serilog.Extensions.Logging.Benchmarks.csproj @@ -0,0 +1,26 @@ + + + + netcoreapp2.2 + true + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/test/Serilog.Extensions.Logging.Benchmarks/Support/CapturingSink.cs b/test/Serilog.Extensions.Logging.Benchmarks/Support/CapturingSink.cs new file mode 100644 index 0000000..3913561 --- /dev/null +++ b/test/Serilog.Extensions.Logging.Benchmarks/Support/CapturingSink.cs @@ -0,0 +1,36 @@ +// Copyright 2019 Serilog Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Serilog.Core; +using Serilog.Events; + +namespace Serilog.Extensions.Logging.Benchmarks.Support +{ + class CapturingSink : ILogEventSink + { + LogEvent _emitted; + + public void Emit(LogEvent logEvent) + { + _emitted = logEvent; + } + + public LogEvent Collect() + { + var collected = _emitted; + _emitted = null; + return collected; + } + } +} diff --git a/test/Serilog.Extensions.Logging.Tests/SerilogLoggerTests.cs b/test/Serilog.Extensions.Logging.Tests/SerilogLoggerTests.cs index b778a44..d2d5bac 100644 --- a/test/Serilog.Extensions.Logging.Tests/SerilogLoggerTests.cs +++ b/test/Serilog.Extensions.Logging.Tests/SerilogLoggerTests.cs @@ -25,7 +25,7 @@ static Tuple SetUp(LogLevel logLevel) var serilogLogger = new LoggerConfiguration() .WriteTo.Sink(sink) - .MinimumLevel.Is(LevelMapping.ToSerilogLevel(logLevel)) + .MinimumLevel.Is(LevelConvert.ToSerilogLevel(logLevel)) .CreateLogger(); var provider = new SerilogLoggerProvider(serilogLogger);