From b2f07728909eb6a4c9f96fb687dde82e713087f0 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 27 May 2020 10:12:36 +1000 Subject: [PATCH 1/7] Dev version bump [skip ci] --- .../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 6b5b367..f8bddc8 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.1.0 + 3.1.1 Microsoft;Serilog Contributors netstandard2.0 true From 00fb5e7d0881716745c131db9f719414f2b6d204 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 23 Jul 2020 15:11:25 +1000 Subject: [PATCH 2/7] Quick README fix - show `services` in the `UseSerilog()` callback signature --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eb17258..c994f10 100644 --- a/README.md +++ b/README.md @@ -79,12 +79,12 @@ You can alternatively configure Serilog using a delegate as shown below: ```csharp // dotnet add package Serilog.Settings.Configuration - .UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration + .UseSerilog((hostingContext, services, loggerConfiguration) => loggerConfiguration .ReadFrom.Configuration(hostingContext.Configuration) .Enrich.FromLogContext() .WriteTo.Console()) ``` -This has the advantage of making the `hostingContext`'s `Configuration` object available for configuration of the logger, but at the expense of recording `Exception`s raised earlier in program startup. +This has the advantage of making the `hostingContext`'s `Configuration` object available for configuration of the logger, but at the expense of ignoring `Exception`s raised earlier in program startup. If this method is used, `Log.Logger` is assigned implicitly, and closed when the app is shut down. From ffb726b5f297a7b2e851062fb0dafce54e965e2b Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 8 Oct 2020 13:09:08 +1000 Subject: [PATCH 3/7] A bootstrap logger (#28) * ReadFrom.Services() * A have-your-cake-and-eat-it-too bootstrap logger * Delete accidentally-committed log file * Remove methods covered by default interface implementations --- .gitignore | 1 + samples/WebApplicationSample/Program.cs | 49 ++ .../Properties/launchSettings.json | 27 + samples/WebApplicationSample/Startup.cs | 42 ++ .../WebApplicationSample.csproj | 17 + .../appsettings.Development.json | 2 + samples/WebApplicationSample/appsettings.json | 18 + serilog-extensions-hosting.sln | 7 + .../Hosting/CachingReloadableLogger.cs | 519 ++++++++++++++ .../Hosting/FixedPropertyEnricher.cs | 34 + .../Extensions/Hosting/IReloadableLogger.cs | 21 + .../Hosting/InjectedLoggerSettings.cs | 39 + .../Extensions/Hosting/ReloadableLogger.cs | 669 ++++++++++++++++++ .../LoggerConfigurationExtensions.cs | 40 ++ .../LoggerSettingsConfigurationExtensions.cs | 43 ++ .../Serilog.Extensions.Hosting.csproj | 12 +- .../SerilogHostBuilderExtensions.cs | 41 +- ...gerSettingsConfigurationExtensionsTests.cs | 29 + .../ReloadableLoggerTests.cs | 22 + .../Serilog.Extensions.Hosting.Tests.csproj | 4 +- 20 files changed, 1621 insertions(+), 15 deletions(-) create mode 100644 samples/WebApplicationSample/Program.cs create mode 100644 samples/WebApplicationSample/Properties/launchSettings.json create mode 100644 samples/WebApplicationSample/Startup.cs create mode 100644 samples/WebApplicationSample/WebApplicationSample.csproj create mode 100644 samples/WebApplicationSample/appsettings.Development.json create mode 100644 samples/WebApplicationSample/appsettings.json create mode 100644 src/Serilog.Extensions.Hosting/Extensions/Hosting/CachingReloadableLogger.cs create mode 100644 src/Serilog.Extensions.Hosting/Extensions/Hosting/FixedPropertyEnricher.cs create mode 100644 src/Serilog.Extensions.Hosting/Extensions/Hosting/IReloadableLogger.cs create mode 100644 src/Serilog.Extensions.Hosting/Extensions/Hosting/InjectedLoggerSettings.cs create mode 100644 src/Serilog.Extensions.Hosting/Extensions/Hosting/ReloadableLogger.cs create mode 100644 src/Serilog.Extensions.Hosting/LoggerConfigurationExtensions.cs create mode 100644 src/Serilog.Extensions.Hosting/LoggerSettingsConfigurationExtensions.cs create mode 100644 test/Serilog.Extensions.Hosting.Tests/LoggerSettingsConfigurationExtensionsTests.cs create mode 100644 test/Serilog.Extensions.Hosting.Tests/ReloadableLoggerTests.cs diff --git a/.gitignore b/.gitignore index a52a718..0173fad 100644 --- a/.gitignore +++ b/.gitignore @@ -285,3 +285,4 @@ __pycache__/ *.btm.cs *.odx.cs *.xsd.cs +/samples/WebApplicationSample/logs/ diff --git a/samples/WebApplicationSample/Program.cs b/samples/WebApplicationSample/Program.cs new file mode 100644 index 0000000..3fcae13 --- /dev/null +++ b/samples/WebApplicationSample/Program.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Serilog; + +namespace WebApplicationSample +{ + public static class Program + { + public static int Main(string[] args) + { + Log.Logger = new LoggerConfiguration() + .WriteTo.Console() + .CreateBootstrapLogger(); + + Log.Information("Starting up!"); + + try + { + CreateHostBuilder(args).Build().Run(); + + Log.Information("Stopped cleanly"); + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "An unhandled exception occured during bootstrapping"); + return 1; + } + finally + { + Log.CloseAndFlush(); + } + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseSerilog((context, services, configuration) => configuration + .WriteTo.Console() + .ReadFrom.Configuration(context.Configuration) + .ReadFrom.Services(services)) + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); + } +} \ No newline at end of file diff --git a/samples/WebApplicationSample/Properties/launchSettings.json b/samples/WebApplicationSample/Properties/launchSettings.json new file mode 100644 index 0000000..932948b --- /dev/null +++ b/samples/WebApplicationSample/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:15670", + "sslPort": 44322 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "WebApplicationSample": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/WebApplicationSample/Startup.cs b/samples/WebApplicationSample/Startup.cs new file mode 100644 index 0000000..4fe346d --- /dev/null +++ b/samples/WebApplicationSample/Startup.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Serilog; + +namespace WebApplicationSample +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapGet("/", async context => + { + Log.Information("Saying hello"); + await context.Response.WriteAsync("Hello World!"); + }); + }); + } + } +} \ No newline at end of file diff --git a/samples/WebApplicationSample/WebApplicationSample.csproj b/samples/WebApplicationSample/WebApplicationSample.csproj new file mode 100644 index 0000000..9f85c41 --- /dev/null +++ b/samples/WebApplicationSample/WebApplicationSample.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + diff --git a/samples/WebApplicationSample/appsettings.Development.json b/samples/WebApplicationSample/appsettings.Development.json new file mode 100644 index 0000000..2c63c08 --- /dev/null +++ b/samples/WebApplicationSample/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} diff --git a/samples/WebApplicationSample/appsettings.json b/samples/WebApplicationSample/appsettings.json new file mode 100644 index 0000000..633356d --- /dev/null +++ b/samples/WebApplicationSample/appsettings.json @@ -0,0 +1,18 @@ +{ + "Serilog": { + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "WriteTo": [ + { + "Name": "File", + "Args": { "path": "./logs/log-.txt", "rollingInterval": "Day" } + } + ] + }, + "AllowedHosts": "*" +} diff --git a/serilog-extensions-hosting.sln b/serilog-extensions-hosting.sln index e5c0bf6..2208752 100644 --- a/serilog-extensions-hosting.sln +++ b/serilog-extensions-hosting.sln @@ -24,6 +24,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Extensions.Hosting. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleServiceSample", "samples\SimpleServiceSample\SimpleServiceSample.csproj", "{E5A82756-4619-4E6B-8B26-6D83E00E99F0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApplicationSample", "samples\WebApplicationSample\WebApplicationSample.csproj", "{1ACDCA67-F404-45AB-9348-98E55E03CB8C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -42,6 +44,10 @@ Global {E5A82756-4619-4E6B-8B26-6D83E00E99F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {E5A82756-4619-4E6B-8B26-6D83E00E99F0}.Release|Any CPU.ActiveCfg = Release|Any CPU {E5A82756-4619-4E6B-8B26-6D83E00E99F0}.Release|Any CPU.Build.0 = Release|Any CPU + {1ACDCA67-F404-45AB-9348-98E55E03CB8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1ACDCA67-F404-45AB-9348-98E55E03CB8C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1ACDCA67-F404-45AB-9348-98E55E03CB8C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1ACDCA67-F404-45AB-9348-98E55E03CB8C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -50,6 +56,7 @@ Global {0549D23F-986B-4FB2-BACE-16FD7A7BC9EF} = {A1893BD1-333D-4DFE-A0F0-DDBB2FE526E0} {AD51759B-CD58-473F-9620-0B0E56A123A1} = {E30F638E-BBBE-4AD1-93CE-48CC69CFEFE1} {E5A82756-4619-4E6B-8B26-6D83E00E99F0} = {F2407211-6043-439C-8E06-3641634332E7} + {1ACDCA67-F404-45AB-9348-98E55E03CB8C} = {F2407211-6043-439C-8E06-3641634332E7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {811E61C5-3871-4633-AFAE-B35B619C8A10} diff --git a/src/Serilog.Extensions.Hosting/Extensions/Hosting/CachingReloadableLogger.cs b/src/Serilog.Extensions.Hosting/Extensions/Hosting/CachingReloadableLogger.cs new file mode 100644 index 0000000..9a47560 --- /dev/null +++ b/src/Serilog.Extensions.Hosting/Extensions/Hosting/CachingReloadableLogger.cs @@ -0,0 +1,519 @@ +// 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 System; +using System.Collections.Generic; +using System.Threading; +using Serilog.Core; +using Serilog.Events; + +namespace Serilog.Extensions.Hosting +{ + class CachingReloadableLogger : ILogger, IReloadableLogger + { + readonly ReloadableLogger _reloadableLogger; + readonly Func _configure; + readonly IReloadableLogger _parent; + + ILogger _root, _cached; + bool _frozen; + + public CachingReloadableLogger(ReloadableLogger reloadableLogger, ILogger root, IReloadableLogger parent, Func configure) + { + _reloadableLogger = reloadableLogger; + _parent = parent; + _configure = configure; + _root = root; + _cached = null; + _frozen = false; + } + + public ILogger ReloadLogger() + { + return _configure(_parent.ReloadLogger()); + } + + public ILogger ForContext(ILogEventEnricher enricher) + { + if (enricher == null) return this; + + if (_frozen) + return _cached.ForContext(enricher); + + if (_reloadableLogger.CreateChild( + _root, + this, + _cached, + p => p.ForContext(enricher), + out var child, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + + return child; + } + + public ILogger ForContext(IEnumerable enrichers) + { + if (enrichers == null) return this; + + if (_frozen) + return _cached.ForContext(enrichers); + + + if (_reloadableLogger.CreateChild( + _root, + this, + _cached, + p => p.ForContext(enrichers), + out var child, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + + return child; + } + + public ILogger ForContext(string propertyName, object value, bool destructureObjects = false) + { + if (propertyName == null) return this; + + if (_frozen) + return _cached.ForContext(propertyName, value, destructureObjects); + + // There's a trade-off, here. Changes to destructuring configuration won't be picked up, but, + // it's better to not extend the lifetime of `value` or pass it between threads unexpectedly. + var eager = ReloadLogger(); + if (!eager.BindProperty(propertyName, value, destructureObjects, out var property)) + return this; + + var enricher = new FixedPropertyEnricher(property); + + if (_reloadableLogger.CreateChild( + _root, + this, + _cached, + p => p.ForContext(enricher), + out var child, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + + return child; + } + + public ILogger ForContext() + { + if (_frozen) + return _cached.ForContext(); + + + if (_reloadableLogger.CreateChild( + _root, + this, + _cached, + p => p.ForContext(), + out var child, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + + return child; + } + + public ILogger ForContext(Type source) + { + if (_frozen) + return _cached.ForContext(source); + + if (_reloadableLogger.CreateChild( + _root, + this, + _cached, + p => p.ForContext(source), + out var child, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + + return child; + } + + void Update(ILogger newRoot, ILogger newCached, bool frozen) + { + _root = newRoot; + _cached = newCached; + _frozen = frozen; + + // https://github.com/dotnet/runtime/issues/20500#issuecomment-284774431 + // Publish `_cached` and `_frozen`. This is useful here because it means that once the logger is frozen - which + // we always expect - reads don't require any synchronization/interlocked instructions. + Interlocked.MemoryBarrierProcessWide(); + } + + public void Write(LogEvent logEvent) + { + if (_frozen) + { + _cached.Write(logEvent); + return; + } + + if (_reloadableLogger.InvokeWrite( + _root, + _cached, + this, + logEvent, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + } + + public void Write(LogEventLevel level, string messageTemplate) + { + if (_frozen) + { + _cached.Write(level, messageTemplate); + return; + } + + if (_reloadableLogger.InvokeWrite( + _root, + _cached, + this, + level, + messageTemplate, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + } + + public void Write(LogEventLevel level, string messageTemplate, T propertyValue) + { + if (_frozen) + { + _cached.Write(level, messageTemplate, propertyValue); + return; + } + + if (_reloadableLogger.InvokeWrite( + _root, + _cached, + this, + level, + messageTemplate, + propertyValue, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + } + + public void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + if (_frozen) + { + _cached.Write(level, messageTemplate, propertyValue0, propertyValue1); + return; + } + + if (_reloadableLogger.InvokeWrite( + _root, + _cached, + this, + level, + messageTemplate, + propertyValue0, + propertyValue1, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + } + + public void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1, + T2 propertyValue2) + { + if (_frozen) + { + _cached.Write(level, messageTemplate, propertyValue0, propertyValue1, propertyValue2); + return; + } + + if (_reloadableLogger.InvokeWrite( + _root, + _cached, + this, + level, + messageTemplate, + propertyValue0, + propertyValue1, + propertyValue2, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + } + + public void Write(LogEventLevel level, string messageTemplate, params object[] propertyValues) + { + if (_frozen) + { + _cached.Write(level, messageTemplate, propertyValues); + return; + } + + if (_reloadableLogger.InvokeWrite( + _root, + _cached, + this, + level, + messageTemplate, + propertyValues, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate) + { + if (_frozen) + { + _cached.Write(level, exception, messageTemplate); + return; + } + + if (_reloadableLogger.InvokeWrite( + _root, + _cached, + this, + level, + exception, + messageTemplate, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate, T propertyValue) + { + if (_frozen) + { + _cached.Write(level, exception, messageTemplate, propertyValue); + return; + } + + if (_reloadableLogger.InvokeWrite( + _root, + _cached, + this, + level, + exception, + messageTemplate, + propertyValue, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate, T0 propertyValue0, + T1 propertyValue1) + { + if (_frozen) + { + _cached.Write(level, exception, messageTemplate, propertyValue0, propertyValue1); + return; + } + + if (_reloadableLogger.InvokeWrite( + _root, + _cached, + this, + level, + exception, + messageTemplate, + propertyValue0, + propertyValue1, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate, T0 propertyValue0, + T1 propertyValue1, T2 propertyValue2) + { + if (_frozen) + { + _cached.Write(level, exception, messageTemplate, propertyValue0, propertyValue1, propertyValue2); + return; + } + + if (_reloadableLogger.InvokeWrite( + _root, + _cached, + this, + level, + exception, + messageTemplate, + propertyValue0, + propertyValue1, + propertyValue2, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate, params object[] propertyValues) + { + if (_frozen) + { + _cached.Write(level, exception, messageTemplate, propertyValues); + return; + } + + if (_reloadableLogger.InvokeWrite( + _root, + _cached, + this, + level, + exception, + messageTemplate, + propertyValues, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + } + + public bool IsEnabled(LogEventLevel level) + { + if (_frozen) + { + return _cached.IsEnabled(level); + } + + if (_reloadableLogger.InvokeIsEnabled( + _root, + _cached, + this, + level, + out var isEnabled, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + + return isEnabled; + } + + public bool BindMessageTemplate(string messageTemplate, object[] propertyValues, out MessageTemplate parsedTemplate, + out IEnumerable boundProperties) + { + if (_frozen) + { + return _cached.BindMessageTemplate(messageTemplate, propertyValues, out parsedTemplate, out boundProperties); + } + + if (_reloadableLogger.InvokeBindMessageTemplate( + _root, + _cached, + this, + messageTemplate, + propertyValues, + out parsedTemplate, + out boundProperties, + out var canBind, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + + return canBind; + } + + public bool BindProperty(string propertyName, object value, bool destructureObjects, out LogEventProperty property) + { + if (_frozen) + { + return _cached.BindProperty(propertyName, value, destructureObjects, out property); + } + + if (_reloadableLogger.InvokeBindProperty( + _root, + _cached, + this, + propertyName, + value, + destructureObjects, + out property, + out var canBind, + out var newRoot, + out var newCached, + out var frozen)) + { + Update(newRoot, newCached, frozen); + } + + return canBind; + } + } +} \ No newline at end of file diff --git a/src/Serilog.Extensions.Hosting/Extensions/Hosting/FixedPropertyEnricher.cs b/src/Serilog.Extensions.Hosting/Extensions/Hosting/FixedPropertyEnricher.cs new file mode 100644 index 0000000..3543173 --- /dev/null +++ b/src/Serilog.Extensions.Hosting/Extensions/Hosting/FixedPropertyEnricher.cs @@ -0,0 +1,34 @@ +// 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 +{ + class FixedPropertyEnricher : ILogEventEnricher + { + readonly LogEventProperty _property; + + public FixedPropertyEnricher(LogEventProperty property) + { + _property = property; + } + + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) + { + logEvent.AddPropertyIfAbsent(_property); + } + } +} diff --git a/src/Serilog.Extensions.Hosting/Extensions/Hosting/IReloadableLogger.cs b/src/Serilog.Extensions.Hosting/Extensions/Hosting/IReloadableLogger.cs new file mode 100644 index 0000000..07b4cf9 --- /dev/null +++ b/src/Serilog.Extensions.Hosting/Extensions/Hosting/IReloadableLogger.cs @@ -0,0 +1,21 @@ +// 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. + +namespace Serilog.Extensions.Hosting +{ + interface IReloadableLogger + { + ILogger ReloadLogger(); + } +} \ No newline at end of file diff --git a/src/Serilog.Extensions.Hosting/Extensions/Hosting/InjectedLoggerSettings.cs b/src/Serilog.Extensions.Hosting/Extensions/Hosting/InjectedLoggerSettings.cs new file mode 100644 index 0000000..97e37ea --- /dev/null +++ b/src/Serilog.Extensions.Hosting/Extensions/Hosting/InjectedLoggerSettings.cs @@ -0,0 +1,39 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Serilog.Configuration; +using Serilog.Core; + +namespace Serilog.Extensions.Hosting +{ + class InjectedLoggerSettings : ILoggerSettings + { + readonly IServiceProvider _services; + + public InjectedLoggerSettings(IServiceProvider services) + { + _services = services ?? throw new ArgumentNullException(nameof(services)); + } + + public void Configure(LoggerConfiguration loggerConfiguration) + { + var levelSwitch = _services.GetService(); + if (levelSwitch != null) + loggerConfiguration.MinimumLevel.ControlledBy(levelSwitch); + + foreach (var settings in _services.GetServices()) + loggerConfiguration.ReadFrom.Settings(settings); + + foreach (var policy in _services.GetServices()) + loggerConfiguration.Destructure.With(policy); + + foreach (var enricher in _services.GetServices()) + loggerConfiguration.Enrich.With(enricher); + + foreach (var filter in _services.GetServices()) + loggerConfiguration.Filter.With(filter); + + foreach (var sink in _services.GetServices()) + loggerConfiguration.WriteTo.Sink(sink); + } + } +} diff --git a/src/Serilog.Extensions.Hosting/Extensions/Hosting/ReloadableLogger.cs b/src/Serilog.Extensions.Hosting/Extensions/Hosting/ReloadableLogger.cs new file mode 100644 index 0000000..454b707 --- /dev/null +++ b/src/Serilog.Extensions.Hosting/Extensions/Hosting/ReloadableLogger.cs @@ -0,0 +1,669 @@ +// 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 System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; +using Serilog.Core; +using Serilog.Events; + +// ReSharper disable MemberCanBePrivate.Global + +namespace Serilog.Extensions.Hosting +{ + /// + /// A Serilog that can be reconfigured without invalidating existing + /// instances derived from it. + /// + public sealed class ReloadableLogger : ILogger, IReloadableLogger, IDisposable + { + readonly object _sync = new object(); + Logger _logger; + + // One-way; if the value is `true` it can never again be made `false`, allowing "double-checked" reads. If + // `true`, `_logger` is final and a memory barrier ensures the final value is seen by all threads. + bool _frozen; + + // Unsure whether this should be exposed; currently going for minimal API surface. + internal ReloadableLogger(Logger initial) + { + _logger = initial ?? throw new ArgumentNullException(nameof(initial)); + } + + ILogger IReloadableLogger.ReloadLogger() + { + return _logger; + } + + /// + /// Reload the logger using the supplied configuration delegate. + /// + /// A callback in which the logger is reconfigured. + /// is null. + public void Reload(Func configure) + { + if (configure == null) throw new ArgumentNullException(nameof(configure)); + + lock (_sync) + { + _logger.Dispose(); + _logger = configure(new LoggerConfiguration()).CreateLogger(); + } + } + + /// + /// Freeze the logger, so that no further reconfiguration is possible. Once the logger is frozen, logging through + /// new contextual loggers will have no additional cost, and logging directly through this logger will not require + /// any synchronization. + /// + /// The configured with the final settings. + /// The logger is already frozen. + public Logger Freeze() + { + lock (_sync) + { + if (_frozen) + throw new InvalidOperationException("The logger is already frozen."); + + _frozen = true; + + // https://github.com/dotnet/runtime/issues/20500#issuecomment-284774431 + // Publish `_logger` and `_frozen`. This is useful here because it means that once the logger is frozen - which + // we always expect - reads don't require any synchronization/interlocked instructions. + Interlocked.MemoryBarrierProcessWide(); + + return _logger; + } + } + + /// + public void Dispose() + { + lock (_sync) + _logger.Dispose(); + } + + /// + public ILogger ForContext(ILogEventEnricher enricher) + { + if (enricher == null) return this; + + if (_frozen) + return _logger.ForContext(enricher); + + lock (_sync) + return new CachingReloadableLogger(this, _logger, this, p => p.ForContext(enricher)); + } + + /// + public ILogger ForContext(IEnumerable enrichers) + { + if (enrichers == null) return this; + + if (_frozen) + return _logger.ForContext(enrichers); + + lock (_sync) + return new CachingReloadableLogger(this, _logger, this, p => p.ForContext(enrichers)); + } + + /// + public ILogger ForContext(string propertyName, object value, bool destructureObjects = false) + { + if (propertyName == null) return this; + + if (_frozen) + return _logger.ForContext(propertyName, value, destructureObjects); + + lock (_sync) + return new CachingReloadableLogger(this, _logger, this, p => p.ForContext(propertyName, value, destructureObjects)); + } + + /// + public ILogger ForContext() + { + if (_frozen) + return _logger.ForContext(); + + lock (_sync) + return new CachingReloadableLogger(this, _logger, this, p => p.ForContext()); + } + + /// + public ILogger ForContext(Type source) + { + if (source == null) return this; + + if (_frozen) + return _logger.ForContext(source); + + lock (_sync) + return new CachingReloadableLogger(this, _logger, this, p => p.ForContext(source)); + } + + /// + public void Write(LogEvent logEvent) + { + if (_frozen) + { + _logger.Write(logEvent); + return; + } + + lock (_sync) + { + _logger.Write(logEvent); + } + } + + /// + public void Write(LogEventLevel level, string messageTemplate) + { + if (_frozen) + { + _logger.Write(level, messageTemplate); + return; + } + + lock (_sync) + { + _logger.Write(level, messageTemplate); + } + } + + /// + public void Write(LogEventLevel level, string messageTemplate, T propertyValue) + { + if (_frozen) + { + _logger.Write(level, messageTemplate, propertyValue); + return; + } + + lock (_sync) + { + _logger.Write(level, messageTemplate, propertyValue); + } + } + + /// + public void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + if (_frozen) + { + _logger.Write(level, messageTemplate, propertyValue0, propertyValue1); + return; + } + + lock (_sync) + { + _logger.Write(level, messageTemplate, propertyValue0, propertyValue1); + } + } + + /// + public void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1, + T2 propertyValue2) + { + if (_frozen) + { + _logger.Write(level, messageTemplate, propertyValue0, propertyValue1, propertyValue2); + return; + } + + lock (_sync) + { + _logger.Write(level, messageTemplate, propertyValue0, propertyValue1, propertyValue2); + } + } + + /// + public void Write(LogEventLevel level, string messageTemplate, params object[] propertyValues) + { + if (_frozen) + { + _logger.Write(level, messageTemplate, propertyValues); + return; + } + + lock (_sync) + { + _logger.Write(level, messageTemplate, propertyValues); + } + } + + /// + public void Write(LogEventLevel level, Exception exception, string messageTemplate) + { + if (_frozen) + { + _logger.Write(level, exception, messageTemplate); + return; + } + + lock (_sync) + { + _logger.Write(level, exception, messageTemplate); + } + } + + /// + public void Write(LogEventLevel level, Exception exception, string messageTemplate, T propertyValue) + { + if (_frozen) + { + _logger.Write(level, exception, messageTemplate, propertyValue); + return; + } + + lock (_sync) + { + _logger.Write(level, exception, messageTemplate, propertyValue); + } + } + + /// + public void Write(LogEventLevel level, Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + if (_frozen) + { + _logger.Write(level, exception, messageTemplate, propertyValue0, propertyValue1); + return; + } + + lock (_sync) + { + _logger.Write(level, exception, messageTemplate, propertyValue0, propertyValue1); + } + } + + /// + public void Write(LogEventLevel level, Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, + T2 propertyValue2) + { + if (_frozen) + { + _logger.Write(level, exception, messageTemplate, propertyValue0, propertyValue1, propertyValue2); + return; + } + + lock (_sync) + { + _logger.Write(level, exception, messageTemplate, propertyValue0, propertyValue1, propertyValue2); + } + } + + /// + public void Write(LogEventLevel level, Exception exception, string messageTemplate, params object[] propertyValues) + { + if (_frozen) + { + _logger.Write(level, exception, messageTemplate, propertyValues); + return; + } + + lock (_sync) + { + _logger.Write(level, exception, messageTemplate, propertyValues); + } + } + + /// + public bool IsEnabled(LogEventLevel level) + { + if (_frozen) + { + return _logger.IsEnabled(level); + } + + lock (_sync) + { + return _logger.IsEnabled(level); + } + } + + /// + public bool BindMessageTemplate(string messageTemplate, object[] propertyValues, out MessageTemplate parsedTemplate, + out IEnumerable boundProperties) + { + if (_frozen) + { + return _logger.BindMessageTemplate(messageTemplate, propertyValues, out parsedTemplate, out boundProperties); + } + + lock (_sync) + { + return _logger.BindMessageTemplate(messageTemplate, propertyValues, out parsedTemplate, out boundProperties); + } + } + + /// + public bool BindProperty(string propertyName, object value, bool destructureObjects, out LogEventProperty property) + { + if (_frozen) + { + return _logger.BindProperty(propertyName, value, destructureObjects, out property); + } + + lock (_sync) + { + return _logger.BindProperty(propertyName, value, destructureObjects, out property); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + (ILogger, bool) UpdateForCaller(ILogger root, ILogger cached, IReloadableLogger caller, out ILogger newRoot, out ILogger newCached, out bool frozen) + { + if (cached != null && root == _logger) + { + newRoot = default; + newCached = default; + frozen = _frozen; + return (cached, frozen); // If we're frozen, then the caller hasn't observed this yet and should update. + } + + newRoot = _logger; + newCached = caller.ReloadLogger(); + frozen = false; + return (newCached, true); + } + + internal bool InvokeIsEnabled(ILogger root, ILogger cached, IReloadableLogger caller, LogEventLevel level, out bool isEnabled, out ILogger newRoot, out ILogger newCached, out bool frozen) + { + if (_frozen) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + isEnabled = logger.IsEnabled(level); + return update; + } + + lock (_sync) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + isEnabled = logger.IsEnabled(level); + return update; + } + } + + internal bool InvokeBindMessageTemplate(ILogger root, ILogger cached, IReloadableLogger caller, string messageTemplate, + object[] propertyValues, out MessageTemplate parsedTemplate, out IEnumerable boundProperties, + out bool canBind, out ILogger newRoot, out ILogger newCached, out bool frozen) + { + if (_frozen) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + canBind = logger.BindMessageTemplate(messageTemplate, propertyValues, out parsedTemplate, out boundProperties); + return update; + } + + lock (_sync) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + canBind = logger.BindMessageTemplate(messageTemplate, propertyValues, out parsedTemplate, out boundProperties); + return update; + } + } + + internal bool InvokeBindProperty(ILogger root, ILogger cached, IReloadableLogger caller, string propertyName, + object propertyValue, bool destructureObjects, out LogEventProperty property, + out bool canBind, out ILogger newRoot, out ILogger newCached, out bool frozen) + { + if (_frozen) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + canBind = logger.BindProperty(propertyName, propertyValue, destructureObjects, out property); + return update; + } + + lock (_sync) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + canBind = logger.BindProperty(propertyName, propertyValue, destructureObjects, out property); + return update; + } + } + + internal bool InvokeWrite(ILogger root, ILogger cached, IReloadableLogger caller, LogEvent logEvent, out ILogger newRoot, out ILogger newCached, out bool frozen) + { + if (_frozen) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(logEvent); + return update; + } + + lock (_sync) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(logEvent); + return update; + } + } + + internal bool InvokeWrite(ILogger root, ILogger cached, IReloadableLogger caller, LogEventLevel level, string messageTemplate, + out ILogger newRoot, out ILogger newCached, out bool frozen) + { + if (_frozen) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, messageTemplate); + return update; + } + + lock (_sync) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, messageTemplate); + return update; + } + } + + internal bool InvokeWrite(ILogger root, ILogger cached, IReloadableLogger caller, LogEventLevel level, string messageTemplate, + T propertyValue, + out ILogger newRoot, out ILogger newCached, out bool frozen) + { + if (_frozen) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, messageTemplate, propertyValue); + return update; + } + + lock (_sync) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, messageTemplate, propertyValue); + return update; + } + } + + internal bool InvokeWrite(ILogger root, ILogger cached, IReloadableLogger caller, LogEventLevel level, string messageTemplate, + T0 propertyValue0, T1 propertyValue1, + out ILogger newRoot, out ILogger newCached, out bool frozen) + { + if (_frozen) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, messageTemplate, propertyValue0, propertyValue1); + return update; + } + + lock (_sync) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, messageTemplate, propertyValue0, propertyValue1); + return update; + } + } + + internal bool InvokeWrite(ILogger root, ILogger cached, IReloadableLogger caller, LogEventLevel level, string messageTemplate, + T0 propertyValue0, T1 propertyValue1, T2 propertyValue2, + out ILogger newRoot, out ILogger newCached, out bool frozen) + { + if (_frozen) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, messageTemplate, propertyValue0, propertyValue1, propertyValue2); + return update; + } + + lock (_sync) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, messageTemplate, propertyValue0, propertyValue1, propertyValue2); + return update; + } + } + + internal bool InvokeWrite(ILogger root, ILogger cached, IReloadableLogger caller, LogEventLevel level, string messageTemplate, + object[] propertyValues, + out ILogger newRoot, out ILogger newCached, out bool frozen) + { + if (_frozen) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, messageTemplate, propertyValues); + return update; + } + + lock (_sync) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, messageTemplate, propertyValues); + return update; + } + } + + internal bool InvokeWrite(ILogger root, ILogger cached, IReloadableLogger caller, LogEventLevel level, Exception exception, string messageTemplate, + out ILogger newRoot, out ILogger newCached, out bool frozen) + { + if (_frozen) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, exception, messageTemplate); + return update; + } + + lock (_sync) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, exception, messageTemplate); + return update; + } + } + + internal bool InvokeWrite(ILogger root, ILogger cached, IReloadableLogger caller, LogEventLevel level, Exception exception, string messageTemplate, + T propertyValue, + out ILogger newRoot, out ILogger newCached, out bool frozen) + { + if (_frozen) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, exception, messageTemplate, propertyValue); + return update; + } + + lock (_sync) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, exception, messageTemplate, propertyValue); + return update; + } + } + + internal bool InvokeWrite(ILogger root, ILogger cached, IReloadableLogger caller, LogEventLevel level, Exception exception, string messageTemplate, + T0 propertyValue0, T1 propertyValue1, + out ILogger newRoot, out ILogger newCached, out bool frozen) + { + if (_frozen) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, exception, messageTemplate, propertyValue0, propertyValue1); + return update; + } + + lock (_sync) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, exception, messageTemplate, propertyValue0, propertyValue1); + return update; + } + } + + internal bool InvokeWrite(ILogger root, ILogger cached, IReloadableLogger caller, LogEventLevel level, Exception exception, string messageTemplate, + T0 propertyValue0, T1 propertyValue1, T2 propertyValue2, + out ILogger newRoot, out ILogger newCached, out bool frozen) + { + if (_frozen) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, exception, messageTemplate, propertyValue0, propertyValue1, propertyValue2); + return update; + } + + lock (_sync) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, exception, messageTemplate, propertyValue0, propertyValue1, propertyValue2); + return update; + } + } + + internal bool InvokeWrite(ILogger root, ILogger cached, IReloadableLogger caller, LogEventLevel level, Exception exception, string messageTemplate, + object[] propertyValues, + out ILogger newRoot, out ILogger newCached, out bool frozen) + { + if (_frozen) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, exception, messageTemplate, propertyValues); + return update; + } + + lock (_sync) + { + var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); + logger.Write(level, exception, messageTemplate, propertyValues); + return update; + } + } + + internal bool CreateChild( + ILogger root, + IReloadableLogger parent, + ILogger cachedParent, + Func configureChild, + out ILogger child, + out ILogger newRoot, + out ILogger newCached, + out bool frozen) + { + if (_frozen) + { + var (logger, _) = UpdateForCaller(root, cachedParent, parent, out newRoot, out newCached, out frozen); + child = configureChild(logger); + return true; // Always an update, since the caller has not observed that the reloadable logger is frozen. + } + + // No synchronization, here - a lot of loggers are created and thrown away again without ever being used, + // so we just return a lazy wrapper. + child = new CachingReloadableLogger(this, root, parent, configureChild); + newRoot = default; + newCached = default; + frozen = default; + return false; + } + } +} diff --git a/src/Serilog.Extensions.Hosting/LoggerConfigurationExtensions.cs b/src/Serilog.Extensions.Hosting/LoggerConfigurationExtensions.cs new file mode 100644 index 0000000..2a78262 --- /dev/null +++ b/src/Serilog.Extensions.Hosting/LoggerConfigurationExtensions.cs @@ -0,0 +1,40 @@ +// 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 Microsoft.Extensions.Hosting; +using Serilog.Extensions.Hosting; +using System; + +namespace Serilog +{ + /// + /// Extends . + /// + public static class LoggerConfigurationExtensions + { + /// + /// Create a for use during host bootstrapping. The + /// + /// configuration overload will detect when is set to a instance, and + /// reconfigure/freeze it so that s created during host bootstrapping continue to work once + /// logger configuration (with access to host services) is completed. + /// + /// + /// + public static ReloadableLogger CreateBootstrapLogger(this LoggerConfiguration loggerConfiguration) + { + return new ReloadableLogger(loggerConfiguration.CreateLogger()); + } + } +} diff --git a/src/Serilog.Extensions.Hosting/LoggerSettingsConfigurationExtensions.cs b/src/Serilog.Extensions.Hosting/LoggerSettingsConfigurationExtensions.cs new file mode 100644 index 0000000..4e9c62c --- /dev/null +++ b/src/Serilog.Extensions.Hosting/LoggerSettingsConfigurationExtensions.cs @@ -0,0 +1,43 @@ +// 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 System; +using Serilog.Configuration; +using Serilog.Core; +using Serilog.Extensions.Hosting; + +namespace Serilog +{ + /// + /// Extends with methods for consuming host services. + /// + public static class LoggerSettingsConfigurationExtensions + { + /// + /// Configure the logger using components from the . If present, the logger will + /// receive implementations/instances of , , + /// , , , and + /// . + /// + /// The `ReadFrom` configuration object. + /// A from which services will be requested. + /// A to support method chaining. + public static LoggerConfiguration Services( + this LoggerSettingsConfiguration loggerSettingsConfiguration, + IServiceProvider services) + { + return loggerSettingsConfiguration.Settings(new InjectedLoggerSettings(services)); + } + } +} \ No newline at end of file diff --git a/src/Serilog.Extensions.Hosting/Serilog.Extensions.Hosting.csproj b/src/Serilog.Extensions.Hosting/Serilog.Extensions.Hosting.csproj index f8bddc8..e424cd7 100644 --- a/src/Serilog.Extensions.Hosting/Serilog.Extensions.Hosting.csproj +++ b/src/Serilog.Extensions.Hosting/Serilog.Extensions.Hosting.csproj @@ -2,9 +2,9 @@ Serilog support for .NET Core logging in hosted services - 3.1.1 + 4.0.0 Microsoft;Serilog Contributors - netstandard2.0 + netstandard2.1 true true Serilog.Extensions.Hosting @@ -23,11 +23,11 @@ - + - - - + + + diff --git a/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs b/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs index 26091a2..0863769 100644 --- a/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs +++ b/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs @@ -18,6 +18,7 @@ using Microsoft.Extensions.Logging; using Serilog.Extensions.Hosting; using Serilog.Extensions.Logging; +// ReSharper disable MemberCanBePrivate.Global namespace Serilog { @@ -85,7 +86,6 @@ public static IHostBuilder UseSerilog( return builder; } - /// Sets Serilog as the logging provider. /// /// A is supplied so that configuration and hosting information can be used. @@ -125,6 +125,10 @@ public static IHostBuilder UseSerilog( /// 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. + /// If the static is a bootstrap logger (created using + /// ), and is + /// not specified, the the bootstrap logger will be reconfigured through the supplied delegate, rather than being + /// replaced entirely or ignored. /// The host builder. public static IHostBuilder UseSerilog( this IHostBuilder builder, @@ -135,6 +139,10 @@ public static IHostBuilder UseSerilog( if (builder == null) throw new ArgumentNullException(nameof(builder)); if (configureLogger == null) throw new ArgumentNullException(nameof(configureLogger)); + // This check is eager; replacing the bootstrap logger after calling this method is not supported. + var reloadable = Log.Logger as ReloadableLogger; + var useReload = reloadable != null && !preserveStaticLogger; + builder.ConfigureServices((context, collection) => { LoggerProviderCollection loggerProviders = null; @@ -145,13 +153,30 @@ public static IHostBuilder UseSerilog( collection.AddSingleton(services => { - var loggerConfiguration = new LoggerConfiguration(); + ILogger logger; + if (useReload) + { + reloadable!.Reload(cfg => + { + if (loggerProviders != null) + cfg.WriteTo.Providers(loggerProviders); + + configureLogger(context, services, cfg); + return cfg; + }); + + logger = reloadable.Freeze(); + } + else + { + var loggerConfiguration = new LoggerConfiguration(); - if (loggerProviders != null) - loggerConfiguration.WriteTo.Providers(loggerProviders); - - configureLogger(context, services, loggerConfiguration); - var logger = loggerConfiguration.CreateLogger(); + if (loggerProviders != null) + loggerConfiguration.WriteTo.Providers(loggerProviders); + + configureLogger(context, services, loggerConfiguration); + logger = loggerConfiguration.CreateLogger(); + } return new RegisteredLogger(logger); }); @@ -180,7 +205,7 @@ public static IHostBuilder UseSerilog( Log.Logger = logger; } - var factory = new SerilogLoggerFactory(registeredLogger, true, loggerProviders); + var factory = new SerilogLoggerFactory(registeredLogger, !useReload, loggerProviders); if (writeToProviders) { diff --git a/test/Serilog.Extensions.Hosting.Tests/LoggerSettingsConfigurationExtensionsTests.cs b/test/Serilog.Extensions.Hosting.Tests/LoggerSettingsConfigurationExtensionsTests.cs new file mode 100644 index 0000000..c4e0ac1 --- /dev/null +++ b/test/Serilog.Extensions.Hosting.Tests/LoggerSettingsConfigurationExtensionsTests.cs @@ -0,0 +1,29 @@ +using Microsoft.Extensions.DependencyInjection; +using Serilog.Core; +using Serilog.Extensions.Hosting.Tests.Support; +using Xunit; + +namespace Serilog.Extensions.Hosting.Tests +{ + public class LoggerSettingsConfigurationExtensionsTests + { + [Fact] + public void SinksAreInjectedFromTheServiceProvider() + { + var sink = new SerilogSink(); + + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSingleton(sink); + using var services = serviceCollection.BuildServiceProvider(); + + using var logger = new LoggerConfiguration() + .ReadFrom.Services(services) + .CreateLogger(); + + logger.Information("Hello, world!"); + + var evt = Assert.Single(sink.Writes); + Assert.Equal("Hello, world!", evt!.MessageTemplate.Text); + } + } +} \ No newline at end of file diff --git a/test/Serilog.Extensions.Hosting.Tests/ReloadableLoggerTests.cs b/test/Serilog.Extensions.Hosting.Tests/ReloadableLoggerTests.cs new file mode 100644 index 0000000..e14f4aa --- /dev/null +++ b/test/Serilog.Extensions.Hosting.Tests/ReloadableLoggerTests.cs @@ -0,0 +1,22 @@ +using Xunit; + +namespace Serilog.Extensions.Hosting.Tests +{ + public class ReloadableLoggerTests + { + [Fact] + public void AFrozenLoggerYieldsSerilogLoggers() + { + var logger = new ReloadableLogger(new LoggerConfiguration().CreateLogger()); + var contextual = logger.ForContext(); + + var nested = contextual.ForContext("test", "test"); + Assert.IsNotType(nested); + + logger.Freeze(); + + nested = contextual.ForContext("test", "test"); + Assert.IsType(nested); + } + } +} diff --git a/test/Serilog.Extensions.Hosting.Tests/Serilog.Extensions.Hosting.Tests.csproj b/test/Serilog.Extensions.Hosting.Tests/Serilog.Extensions.Hosting.Tests.csproj index bb9c750..9214cd1 100644 --- a/test/Serilog.Extensions.Hosting.Tests/Serilog.Extensions.Hosting.Tests.csproj +++ b/test/Serilog.Extensions.Hosting.Tests/Serilog.Extensions.Hosting.Tests.csproj @@ -1,12 +1,13 @@  - netcoreapp2.2 + netcoreapp3.1 Serilog.Extensions.Hosting.Tests ../../assets/Serilog.snk true true true + latest @@ -14,6 +15,7 @@ + all From aae91092a7425c0cbe2c613cc30295bfc9da5b35 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 9 Oct 2020 15:02:54 +1000 Subject: [PATCH 4/7] Resolve packaging warnings --- assets/icon.png | Bin 0 -> 20352 bytes .../Serilog.Extensions.Hosting.csproj | 8 ++++++-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 assets/icon.png diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9bf45a319eec8b1a6a2641c282739944d100c9c1 GIT binary patch literal 20352 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 literal 0 HcmV?d00001 diff --git a/src/Serilog.Extensions.Hosting/Serilog.Extensions.Hosting.csproj b/src/Serilog.Extensions.Hosting/Serilog.Extensions.Hosting.csproj index e424cd7..bc3583f 100644 --- a/src/Serilog.Extensions.Hosting/Serilog.Extensions.Hosting.csproj +++ b/src/Serilog.Extensions.Hosting/Serilog.Extensions.Hosting.csproj @@ -13,9 +13,9 @@ true Serilog.Extensions.Hosting serilog;aspnet;aspnetcore;hosting - http://serilog.net/images/serilog-extension-nuget.png + icon.png https://github.com/serilog/serilog-extensions-hosting - http://www.apache.org/licenses/LICENSE-2.0 + Apache-2.0 https://github.com/serilog/serilog-extensions-hosting git false @@ -30,4 +30,8 @@ + + + + From db8e352334ee306c4210875523197c0ba7b0d1b9 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 9 Oct 2020 15:15:47 +1000 Subject: [PATCH 5/7] Update publishing key; try to get symbol packages publishing again --- appveyor.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index ceb50d8..6cd4a96 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,8 +12,7 @@ skip_commits: deploy: - provider: NuGet api_key: - secure: JAnOxPQMZRp8ousbAW4vYnVtd936AOMp4gDGucCQ1RZrDZW3J+X5QmbLO7OJKtBr - skip_symbols: true + secure: wm1N2hx6N0X0f4bae55X83isfINQNHyZkkR1mA/4XIdOJYEDKVonwQrxdgWxwGgl on: branch: /^(master|dev)$/ - provider: GitHub From ca82f9bad41665207829e758c23c78a72b6ae3b3 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 9 Oct 2020 15:19:26 +1000 Subject: [PATCH 6/7] Nope, still no joy without skip_symbols --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 6cd4a96..cf8271e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,6 +11,7 @@ skip_commits: - README.md deploy: - provider: NuGet + skip_symbols: true api_key: secure: wm1N2hx6N0X0f4bae55X83isfINQNHyZkkR1mA/4XIdOJYEDKVonwQrxdgWxwGgl on: From 13cdfe0ed84bf805813e336b0107972f0dcdc241 Mon Sep 17 00:00:00 2001 From: Alexander Salamatov Date: Fri, 6 Nov 2020 19:37:54 +0300 Subject: [PATCH 7/7] Update README.md fixed broken link to serilog-extensions-hosting --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c994f10..f35ae30 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ That's it! You will see log output like: [22:10:39 INF] The current time is: 12/05/2018 10:10:39 +00:00 ``` -A more complete example, showing _appsettings.json_ configuration, can be found in [the sample project here](https://github.com/serilog/serilog-hosting/tree/dev/samples/SimpleServiceSample). +A more complete example, showing _appsettings.json_ configuration, can be found in [the sample project here](https://github.com/serilog/serilog-extensions-hosting/tree/dev/samples/SimpleServiceSample). ### Using the package