From 7d51e5189b8000dd2317f9fd5638a8497f508504 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Sat, 9 May 2020 09:10:47 +1000 Subject: [PATCH 1/5] Supply services when configuring Serilog inline --- global.json | 2 +- serilog-extensions-hosting.sln.DotSettings | 2 + .../Extensions/Hosting/NullEnricher.cs | 28 +++++ .../SerilogHostBuilderExtensions.cs | 107 ++++++++++++++---- 4 files changed, 113 insertions(+), 26 deletions(-) create mode 100644 src/Serilog.Extensions.Hosting/Extensions/Hosting/NullEnricher.cs diff --git a/global.json b/global.json index 0a37afd..05d0ae3 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "2.2.105" + "version": "3.1.201" } } diff --git a/serilog-extensions-hosting.sln.DotSettings b/serilog-extensions-hosting.sln.DotSettings index 87d9b7b..de0032b 100644 --- a/serilog-extensions-hosting.sln.DotSettings +++ b/serilog-extensions-hosting.sln.DotSettings @@ -2,4 +2,6 @@ True True True + True + True True \ No newline at end of file diff --git a/src/Serilog.Extensions.Hosting/Extensions/Hosting/NullEnricher.cs b/src/Serilog.Extensions.Hosting/Extensions/Hosting/NullEnricher.cs new file mode 100644 index 0000000..af13dfc --- /dev/null +++ b/src/Serilog.Extensions.Hosting/Extensions/Hosting/NullEnricher.cs @@ -0,0 +1,28 @@ +// Copyright 2020 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.Hosting +{ + // Does nothing, but makes it easy to create an `ILogger` from a Serilog `Logger` + // that will not dispose the underlying pipeline when disposed itself. + class NullEnricher : ILogEventEnricher + { + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) + { + } + } +} \ No newline at end of file diff --git a/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs b/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs index 306eadd..e87643d 100644 --- a/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs +++ b/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs @@ -1,4 +1,4 @@ -// Copyright 2019 Serilog Contributors +// Copyright 2020 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,9 +13,9 @@ // limitations under the License. using System; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.DependencyInjection; using Serilog.Extensions.Hosting; using Serilog.Extensions.Logging; @@ -26,16 +26,27 @@ namespace Serilog /// public static class SerilogHostBuilderExtensions { + // Used internally to pass information through the container. + class RegisteredLogger + { + public RegisteredLogger(ILogger logger) + { + Logger = logger; + } + + public ILogger Logger { get; } + } + /// /// Sets Serilog as the logging provider. /// /// The host builder to configure. /// 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. + /// logger is not specified but is true, the method will be + /// called on the static class instead. /// A registered in the Serilog pipeline using the - /// WriteTo.Providers() configuration method, enabling other s to receive events. By + /// WriteTo.Providers() configuration method, enabling other s to receive events. By /// default, only Serilog sinks will receive events. /// The host builder. public static IHostBuilder UseSerilog( @@ -71,14 +82,15 @@ public static IHostBuilder UseSerilog( return builder; } + /// Sets Serilog as the logging provider. /// /// A is supplied so that configuration and hosting information can be used. /// The logger will be shut down when application services are disposed. /// /// The host builder to configure. - /// The delegate for configuring the that will be used to construct a . - /// Indicates whether to preserve the value of . + /// The delegate for configuring the that will be used to construct a . + /// Indicates whether to preserve the value of . /// By default, Serilog does not write events to s registered through /// the Microsoft.Extensions.Logging API. Normally, equivalent Serilog sinks are used in place of providers. Specify /// true to write events to all providers. @@ -91,35 +103,80 @@ public static IHostBuilder UseSerilog( { if (builder == null) throw new ArgumentNullException(nameof(builder)); if (configureLogger == null) throw new ArgumentNullException(nameof(configureLogger)); + return UseSerilog( + builder, + (hostBuilderContext, services, loggerConfiguration) => + configureLogger(hostBuilderContext, loggerConfiguration), + preserveStaticLogger: preserveStaticLogger, + writeToProviders: writeToProviders); + } + + /// Sets Serilog as the logging provider. + /// + /// A is supplied so that configuration and hosting information can be used. + /// The logger will be shut down when application services are disposed. + /// + /// The host builder to configure. + /// The delegate for configuring the that will be used to construct a . + /// Indicates whether to preserve the value of . + /// By default, Serilog does not write events to s registered through + /// the Microsoft.Extensions.Logging API. Normally, equivalent Serilog sinks are used in place of providers. Specify + /// true to write events to all providers. + /// The host builder. + public static IHostBuilder UseSerilog( + this IHostBuilder builder, + Action configureLogger, + bool preserveStaticLogger = false, + bool writeToProviders = false) + { + if (builder == null) throw new ArgumentNullException(nameof(builder)); + if (configureLogger == null) throw new ArgumentNullException(nameof(configureLogger)); builder.ConfigureServices((context, collection) => { - var loggerConfiguration = new LoggerConfiguration(); - LoggerProviderCollection loggerProviders = null; if (writeToProviders) { loggerProviders = new LoggerProviderCollection(); - loggerConfiguration.WriteTo.Providers(loggerProviders); } - - configureLogger(context, loggerConfiguration); - var logger = loggerConfiguration.CreateLogger(); - ILogger registeredLogger = null; - if (preserveStaticLogger) - { - registeredLogger = logger; - } - else + collection.AddSingleton(services => { - // Passing a `null` logger to `SerilogLoggerFactory` results in disposal via - // `Log.CloseAndFlush()`, which additionally replaces the static logger with a no-op. - Log.Logger = logger; - } + var loggerConfiguration = new LoggerConfiguration(); + + if (loggerProviders != null) + loggerConfiguration.WriteTo.Providers(loggerProviders); + + configureLogger(context, services, loggerConfiguration); + var logger = loggerConfiguration.CreateLogger(); + + return new RegisteredLogger(logger); + }); + collection.AddSingleton(services => + { + // How can we register the logger, here, but not have MEDI dispose it? + // Using the `NullEnricher` hack to prevent disposal. + var logger = services.GetRequiredService().Logger; + return logger.ForContext(new NullEnricher()); + }); + collection.AddSingleton(services => { + var logger = services.GetRequiredService().Logger; + + ILogger registeredLogger = null; + if (preserveStaticLogger) + { + registeredLogger = logger; + } + else + { + // Passing a `null` logger to `SerilogLoggerFactory` results in disposal via + // `Log.CloseAndFlush()`, which additionally replaces the static logger with a no-op. + Log.Logger = logger; + } + var factory = new SerilogLoggerFactory(registeredLogger, true, loggerProviders); if (writeToProviders) @@ -130,8 +187,8 @@ public static IHostBuilder UseSerilog( return factory; }); - - ConfigureServices(collection, logger); + + ConfigureServices(collection, null); }); return builder; } From 5e5dfdfdfeba2f9537d536058134a7bcfe798a7f Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Sat, 9 May 2020 09:19:29 +1000 Subject: [PATCH 2/5] Minor version bump --- .../Serilog.Extensions.Hosting.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog.Extensions.Hosting/Serilog.Extensions.Hosting.csproj b/src/Serilog.Extensions.Hosting/Serilog.Extensions.Hosting.csproj index d8a31b7..6b5b367 100644 --- a/src/Serilog.Extensions.Hosting/Serilog.Extensions.Hosting.csproj +++ b/src/Serilog.Extensions.Hosting/Serilog.Extensions.Hosting.csproj @@ -2,7 +2,7 @@ Serilog support for .NET Core logging in hosted services - 3.0.1 + 3.1.0 Microsoft;Serilog Contributors netstandard2.0 true From ffcfe32f18ef56e9c7d73c9c2b0bb7417a439e74 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Sat, 9 May 2020 09:23:41 +1000 Subject: [PATCH 3/5] Update build image --- appveyor.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 9ca24f7..ceb50d8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,7 +1,6 @@ version: '{build}' skip_tags: true -image: Visual Studio 2017 -configuration: Release +image: Visual Studio 2019 test: off build_script: - ps: ./Build.ps1 From ddb885eba0ad504b223b8cbda16bfbd2758311a9 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Sun, 10 May 2020 07:46:43 +1000 Subject: [PATCH 4/5] Added comment --- .../SerilogHostBuilderExtensions.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs b/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs index e87643d..081b10f 100644 --- a/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs +++ b/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs @@ -1,4 +1,4 @@ -// Copyright 2020 Serilog Contributors +// Copyright 2020 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -188,8 +188,10 @@ public static IHostBuilder UseSerilog( return factory; }); + // Null is passed here because we've already (lazily) registered `ILogger` ConfigureServices(collection, null); }); + return builder; } From df44782283f2efa115afc1a03873ba6cc434c854 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Mon, 11 May 2020 08:44:24 +1000 Subject: [PATCH 5/5] More comments --- .../SerilogHostBuilderExtensions.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs b/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs index 081b10f..26091a2 100644 --- a/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs +++ b/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs @@ -26,7 +26,10 @@ namespace Serilog /// public static class SerilogHostBuilderExtensions { - // Used internally to pass information through the container. + // Used internally to pass information through the container. We need to do this because if `logger` is the + // root logger, registering it as a singleton may lead to disposal along with the container by MEDI. This isn't + // always desirable, i.e. we may be handed a logger and `dispose: false`, so wrapping it keeps us in control + // of when the logger is disposed. class RegisteredLogger { public RegisteredLogger(ILogger logger)