diff --git a/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs b/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs
index d588000..7f0ab0d 100644
--- a/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs
+++ b/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs
@@ -13,10 +13,8 @@
// limitations under the License.
using System;
-using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
-using Serilog.Extensions.Hosting;
using Serilog.Extensions.Logging;
// ReSharper disable MemberCanBePrivate.Global
@@ -27,20 +25,6 @@ namespace Serilog
///
public static class SerilogHostBuilderExtensions
{
- // 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)
- {
- Logger = logger;
- }
-
- public ILogger Logger { get; }
- }
-
///
/// Sets Serilog as the logging provider.
///
@@ -63,33 +47,7 @@ public static IHostBuilder UseSerilog(
builder.ConfigureServices((_, collection) =>
{
- if (providers != null)
- {
- collection.AddSingleton(services =>
- {
- var factory = new SerilogLoggerFactory(logger, dispose, providers);
-
- foreach (var provider in services.GetServices())
- factory.AddProvider(provider);
-
- return factory;
- });
- }
- else
- {
- collection.AddSingleton(services => new SerilogLoggerFactory(logger, dispose));
- }
-
- if (logger != null)
- {
- // This won't (and shouldn't) take ownership of the logger.
- collection.AddSingleton(logger);
-
- // Still need to use RegisteredLogger as it is used by ConfigureDiagnosticContext.
- collection.AddSingleton(new RegisteredLogger(logger));
- }
- bool useRegisteredLogger = logger != null;
- ConfigureDiagnosticContext(collection, useRegisteredLogger);
+ collection.AddSerilog(logger, dispose, providers);
});
return builder;
@@ -148,108 +106,16 @@ 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.
-#if !NO_RELOADABLE_LOGGER
- var reloadable = Log.Logger as ReloadableLogger;
- var useReload = reloadable != null && !preserveStaticLogger;
-#else
- const bool useReload = false;
-#endif
-
builder.ConfigureServices((context, collection) =>
{
- LoggerProviderCollection loggerProviders = null;
- if (writeToProviders)
- {
- loggerProviders = new LoggerProviderCollection();
- }
-
- collection.AddSingleton(services =>
- {
- ILogger logger;
-#if !NO_RELOADABLE_LOGGER
- if (useReload)
- {
- reloadable!.Reload(cfg =>
- {
- if (loggerProviders != null)
- cfg.WriteTo.Providers(loggerProviders);
-
- configureLogger(context, services, cfg);
- return cfg;
- });
-
- logger = reloadable.Freeze();
- }
- else
-#endif
- {
- var loggerConfiguration = new LoggerConfiguration();
-
- if (loggerProviders != null)
- loggerConfiguration.WriteTo.Providers(loggerProviders);
-
- configureLogger(context, services, loggerConfiguration);
- 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, !useReload, loggerProviders);
-
- if (writeToProviders)
- {
- foreach (var provider in services.GetServices())
- factory.AddProvider(provider);
- }
-
- return factory;
- });
-
- ConfigureDiagnosticContext(collection, preserveStaticLogger);
+ collection.AddSerilog(
+ (services, loggerConfiguration) =>
+ configureLogger(context, services, loggerConfiguration),
+ preserveStaticLogger: preserveStaticLogger,
+ writeToProviders: writeToProviders);
});
return builder;
}
-
- static void ConfigureDiagnosticContext(IServiceCollection collection, bool useRegisteredLogger)
- {
- if (collection == null) throw new ArgumentNullException(nameof(collection));
-
- // Registered to provide two services...
- // Consumed by e.g. middleware
- collection.AddSingleton(services =>
- {
- ILogger logger = useRegisteredLogger ? services.GetRequiredService().Logger : null;
- return new DiagnosticContext(logger);
- });
- // Consumed by user code
- collection.AddSingleton(services => services.GetRequiredService());
- }
}
}
diff --git a/src/Serilog.Extensions.Hosting/SerilogServiceCollectionExtensions.cs b/src/Serilog.Extensions.Hosting/SerilogServiceCollectionExtensions.cs
new file mode 100644
index 0000000..e6055c0
--- /dev/null
+++ b/src/Serilog.Extensions.Hosting/SerilogServiceCollectionExtensions.cs
@@ -0,0 +1,241 @@
+// 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 Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Serilog.Extensions.Hosting;
+using Serilog.Extensions.Logging;
+// ReSharper disable MemberCanBePrivate.Global
+
+namespace Serilog
+{
+ ///
+ /// Extends with Serilog configuration methods.
+ ///
+ public static class SerilogServiceCollectionExtensions
+ {
+ // 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)
+ {
+ Logger = logger;
+ }
+
+ public ILogger Logger { get; }
+ }
+
+ ///
+ /// Sets Serilog as the logging provider.
+ ///
+ /// The service collection to use.
+ /// 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 registered in the Serilog pipeline using the
+ /// WriteTo.Providers() configuration method, enabling other s to receive events. By
+ /// default, only Serilog sinks will receive events.
+ /// The service collection.
+ public static IServiceCollection AddSerilog(
+ this IServiceCollection collection,
+ ILogger logger = null,
+ bool dispose = false,
+ LoggerProviderCollection providers = null)
+ {
+ if (collection == null) throw new ArgumentNullException(nameof(collection));
+
+ if (providers != null)
+ {
+ collection.AddSingleton(services =>
+ {
+ var factory = new SerilogLoggerFactory(logger, dispose, providers);
+
+ foreach (var provider in services.GetServices())
+ factory.AddProvider(provider);
+
+ return factory;
+ });
+ }
+ else
+ {
+ collection.AddSingleton(services => new SerilogLoggerFactory(logger, dispose));
+ }
+
+ if (logger != null)
+ {
+ // This won't (and shouldn't) take ownership of the logger.
+ collection.AddSingleton(logger);
+
+ // Still need to use RegisteredLogger as it is used by ConfigureDiagnosticContext.
+ collection.AddSingleton(new RegisteredLogger(logger));
+ }
+ bool useRegisteredLogger = logger != null;
+ ConfigureDiagnosticContext(collection, useRegisteredLogger);
+
+ return collection;
+ }
+
+ /// Sets Serilog as the logging provider.
+ /// The service collection to use.
+ /// 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 service collection.
+ public static IServiceCollection AddSerilog(
+ this IServiceCollection collection,
+ Action configureLogger,
+ bool preserveStaticLogger = false,
+ bool writeToProviders = false)
+ {
+ if (collection == null) throw new ArgumentNullException(nameof(collection));
+ if (configureLogger == null) throw new ArgumentNullException(nameof(configureLogger));
+ return AddSerilog(
+ collection,
+ (services, loggerConfiguration) =>
+ configureLogger(loggerConfiguration),
+ preserveStaticLogger: preserveStaticLogger,
+ writeToProviders: writeToProviders);
+ }
+
+ /// Sets Serilog as the logging provider.
+ /// The service collection to use.
+ /// 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.
+ /// If the static is a bootstrap logger (see
+ /// LoggerConfigurationExtensions.CreateBootstrapLogger()), and is
+ /// not specified, the the bootstrap logger will be reconfigured through the supplied delegate, rather than being
+ /// replaced entirely or ignored.
+ /// The service collection.
+ public static IServiceCollection AddSerilog(
+ this IServiceCollection collection,
+ Action configureLogger,
+ bool preserveStaticLogger = false,
+ bool writeToProviders = false)
+ {
+ if (collection == null) throw new ArgumentNullException(nameof(collection));
+ if (configureLogger == null) throw new ArgumentNullException(nameof(configureLogger));
+
+ // This check is eager; replacing the bootstrap logger after calling this method is not supported.
+#if !NO_RELOADABLE_LOGGER
+ var reloadable = Log.Logger as ReloadableLogger;
+ var useReload = reloadable != null && !preserveStaticLogger;
+#else
+ const bool useReload = false;
+#endif
+
+ LoggerProviderCollection loggerProviders = null;
+ if (writeToProviders)
+ {
+ loggerProviders = new LoggerProviderCollection();
+ }
+
+ collection.AddSingleton(services =>
+ {
+ ILogger logger;
+#if !NO_RELOADABLE_LOGGER
+ if (useReload)
+ {
+ reloadable!.Reload(cfg =>
+ {
+ if (loggerProviders != null)
+ cfg.WriteTo.Providers(loggerProviders);
+
+ configureLogger(services, cfg);
+ return cfg;
+ });
+
+ logger = reloadable.Freeze();
+ }
+ else
+#endif
+ {
+ var loggerConfiguration = new LoggerConfiguration();
+
+ if (loggerProviders != null)
+ loggerConfiguration.WriteTo.Providers(loggerProviders);
+
+ configureLogger(services, loggerConfiguration);
+ 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, !useReload, loggerProviders);
+
+ if (writeToProviders)
+ {
+ foreach (var provider in services.GetServices())
+ factory.AddProvider(provider);
+ }
+
+ return factory;
+ });
+
+ ConfigureDiagnosticContext(collection, preserveStaticLogger);
+
+ return collection;
+ }
+
+ static void ConfigureDiagnosticContext(IServiceCollection collection, bool useRegisteredLogger)
+ {
+ if (collection == null) throw new ArgumentNullException(nameof(collection));
+
+ // Registered to provide two services...
+ // Consumed by e.g. middleware
+ collection.AddSingleton(services =>
+ {
+ ILogger logger = useRegisteredLogger ? services.GetRequiredService().Logger : null;
+ return new DiagnosticContext(logger);
+ });
+ // Consumed by user code
+ collection.AddSingleton(services => services.GetRequiredService());
+ }
+ }
+}
diff --git a/test/Serilog.Extensions.Hosting.Tests/SerilogServiceCollectionExtensionsTests.cs b/test/Serilog.Extensions.Hosting.Tests/SerilogServiceCollectionExtensionsTests.cs
new file mode 100644
index 0000000..7fd9752
--- /dev/null
+++ b/test/Serilog.Extensions.Hosting.Tests/SerilogServiceCollectionExtensionsTests.cs
@@ -0,0 +1,58 @@
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Xunit;
+
+namespace Serilog.Extensions.Hosting.Tests
+{
+ public class SerilogServiceCollectionExtensionsTests
+ {
+ [Fact]
+ public void ServicesAreRegisteredWhenCallingAddSerilog()
+ {
+ // Arrange
+ var collection = new ServiceCollection();
+
+ // Act
+ collection.AddSerilog();
+
+ // Assert
+ using var provider = collection.BuildServiceProvider();
+ provider.GetRequiredService();
+ provider.GetRequiredService();
+ }
+
+ [Fact]
+ public void ServicesAreRegisteredWhenCallingAddSerilogWithLogger()
+ {
+ // Arrange
+ var collection = new ServiceCollection();
+ ILogger logger = new LoggerConfiguration().CreateLogger();
+
+ // Act
+ collection.AddSerilog(logger);
+
+ // Assert
+ using var provider = collection.BuildServiceProvider();
+ provider.GetRequiredService();
+ provider.GetRequiredService();
+ provider.GetRequiredService();
+ }
+
+ [Fact]
+ public void ServicesAreRegisteredWhenCallingAddSerilogWithConfigureDelegate()
+ {
+ // Arrange
+ var collection = new ServiceCollection();
+
+ // Act
+ collection.AddSerilog(_ => { });
+
+ // Assert
+ using var provider = collection.BuildServiceProvider();
+ provider.GetRequiredService();
+ provider.GetRequiredService();
+ provider.GetRequiredService();
+ }
+ }
+}