diff --git a/src/Microsoft.AspNetCore.Hosting.Abstractions/IWebHostBuilder.cs b/src/Microsoft.AspNetCore.Hosting.Abstractions/IWebHostBuilder.cs
index 17274117eb..b37fb88b55 100644
--- a/src/Microsoft.AspNetCore.Hosting.Abstractions/IWebHostBuilder.cs
+++ b/src/Microsoft.AspNetCore.Hosting.Abstractions/IWebHostBuilder.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -38,6 +39,13 @@ namespace Microsoft.AspNetCore.Hosting
/// The .
IWebHostBuilder ConfigureLogging(Action configureLogging);
+ ///
+ /// Adds a delegate for configuring the provided . This may be called multiple times.
+ ///
+ /// The delegate that configures the .
+ /// The .
+ IWebHostBuilder ConfigureLogging(Action configureLogging) where T : ILoggerFactory;
+
///
/// Add or replace a setting in the configuration.
///
@@ -52,5 +60,21 @@ namespace Microsoft.AspNetCore.Hosting
/// The key of the setting to look up.
/// The value the setting currently contains.
string GetSetting(string key);
+
+ ///
+ /// Adds a delegate to construct the that will be registered
+ /// as a singleton and used by the application.
+ ///
+ /// The delegate that constructs an
+ /// The .
+ IWebHostBuilder UseLoggerFactory(Func createLoggerFactory);
+
+
+ ///
+ /// Adds a delegate for configuring the that will construct an .
+ ///
+ /// The delegate for configuring the that will be used to construct an .
+ /// The .
+ IWebHostBuilder ConfigureConfiguration(Action configureDelegate);
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Hosting.Abstractions/WebHostBuilderContext.cs b/src/Microsoft.AspNetCore.Hosting.Abstractions/WebHostBuilderContext.cs
new file mode 100644
index 0000000000..5a973abd0a
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Hosting.Abstractions/WebHostBuilderContext.cs
@@ -0,0 +1,29 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+
+namespace Microsoft.AspNetCore.Hosting
+{
+ ///
+ /// Context containing the common services on the . Some properties may be null until set by the .
+ ///
+ public class WebHostBuilderContext
+ {
+ ///
+ /// The initialized by the .
+ ///
+ public IHostingEnvironment HostingEnvironment { get; set; }
+
+ ///
+ /// The containing the merged configuration of the application and the .
+ ///
+ public IConfiguration Configuration { get; set; }
+
+ ///
+ /// The configured on the .
+ ///
+ public ILoggerFactory LoggerFactory { get; set; }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Hosting.Abstractions/exceptions.net45.json b/src/Microsoft.AspNetCore.Hosting.Abstractions/exceptions.net45.json
new file mode 100644
index 0000000000..991748d863
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Hosting.Abstractions/exceptions.net45.json
@@ -0,0 +1,20 @@
+[
+ {
+ "OldTypeId": "public interface Microsoft.AspNetCore.Hosting.IWebHostBuilder",
+ "NewTypeId": "public interface Microsoft.AspNetCore.Hosting.IWebHostBuilder",
+ "NewMemberId": "Microsoft.AspNetCore.Hosting.IWebHostBuilder ConfigureLogging(System.Action configureLogging) where T0 : Microsoft.Extensions.Logging.ILoggerFactory",
+ "Kind": "Addition"
+ },
+ {
+ "OldTypeId": "public interface Microsoft.AspNetCore.Hosting.IWebHostBuilder",
+ "NewTypeId": "public interface Microsoft.AspNetCore.Hosting.IWebHostBuilder",
+ "NewMemberId": "Microsoft.AspNetCore.Hosting.IWebHostBuilder UseLoggerFactory(System.Func createLoggerFactory)",
+ "Kind": "Addition"
+ },
+ {
+ "OldTypeId": "public interface Microsoft.AspNetCore.Hosting.IWebHostBuilder",
+ "NewTypeId": "public interface Microsoft.AspNetCore.Hosting.IWebHostBuilder",
+ "NewMemberId": "Microsoft.AspNetCore.Hosting.IWebHostBuilder ConfigureConfiguration(System.Action configureDelegate)",
+ "Kind": "Addition"
+ }
+]
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Hosting.Abstractions/exceptions.netcore.json b/src/Microsoft.AspNetCore.Hosting.Abstractions/exceptions.netcore.json
new file mode 100644
index 0000000000..991748d863
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Hosting.Abstractions/exceptions.netcore.json
@@ -0,0 +1,20 @@
+[
+ {
+ "OldTypeId": "public interface Microsoft.AspNetCore.Hosting.IWebHostBuilder",
+ "NewTypeId": "public interface Microsoft.AspNetCore.Hosting.IWebHostBuilder",
+ "NewMemberId": "Microsoft.AspNetCore.Hosting.IWebHostBuilder ConfigureLogging(System.Action configureLogging) where T0 : Microsoft.Extensions.Logging.ILoggerFactory",
+ "Kind": "Addition"
+ },
+ {
+ "OldTypeId": "public interface Microsoft.AspNetCore.Hosting.IWebHostBuilder",
+ "NewTypeId": "public interface Microsoft.AspNetCore.Hosting.IWebHostBuilder",
+ "NewMemberId": "Microsoft.AspNetCore.Hosting.IWebHostBuilder UseLoggerFactory(System.Func createLoggerFactory)",
+ "Kind": "Addition"
+ },
+ {
+ "OldTypeId": "public interface Microsoft.AspNetCore.Hosting.IWebHostBuilder",
+ "NewTypeId": "public interface Microsoft.AspNetCore.Hosting.IWebHostBuilder",
+ "NewMemberId": "Microsoft.AspNetCore.Hosting.IWebHostBuilder ConfigureConfiguration(System.Action configureDelegate)",
+ "Kind": "Addition"
+ }
+]
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Hosting/Microsoft.AspNetCore.Hosting.csproj b/src/Microsoft.AspNetCore.Hosting/Microsoft.AspNetCore.Hosting.csproj
index 344c4cc654..1f5c69fcb4 100644
--- a/src/Microsoft.AspNetCore.Hosting/Microsoft.AspNetCore.Hosting.csproj
+++ b/src/Microsoft.AspNetCore.Hosting/Microsoft.AspNetCore.Hosting.csproj
@@ -21,6 +21,7 @@
+
diff --git a/src/Microsoft.AspNetCore.Hosting/WebHostBuilder.cs b/src/Microsoft.AspNetCore.Hosting/WebHostBuilder.cs
index 0b4b2d154c..48a357aed6 100644
--- a/src/Microsoft.AspNetCore.Hosting/WebHostBuilder.cs
+++ b/src/Microsoft.AspNetCore.Hosting/WebHostBuilder.cs
@@ -29,9 +29,10 @@ namespace Microsoft.AspNetCore.Hosting
private readonly List> _configureLoggingDelegates;
private IConfiguration _config;
- private ILoggerFactory _loggerFactory;
private WebHostOptions _options;
private bool _webHostBuilt;
+ private Func _createLoggerFactoryDelegate;
+ private List> _configureConfigurationBuilderDelegates;
///
/// Initializes a new instance of the class.
@@ -41,6 +42,7 @@ namespace Microsoft.AspNetCore.Hosting
_hostingEnvironment = new HostingEnvironment();
_configureServicesDelegates = new List>();
_configureLoggingDelegates = new List>();
+ _configureConfigurationBuilderDelegates = new List>();
_config = new ConfigurationBuilder()
.AddEnvironmentVariables(prefix: "ASPNETCORE_")
@@ -94,7 +96,7 @@ namespace Microsoft.AspNetCore.Hosting
throw new ArgumentNullException(nameof(loggerFactory));
}
- _loggerFactory = loggerFactory;
+ _createLoggerFactoryDelegate = _ => loggerFactory;
return this;
}
@@ -131,6 +133,60 @@ namespace Microsoft.AspNetCore.Hosting
return this;
}
+ ///
+ /// Adds a delegate to construct the that will be registered
+ /// as a singleton and used by the application.
+ ///
+ /// The delegate that constructs an
+ /// The .
+ public IWebHostBuilder UseLoggerFactory(Func createLoggerFactory)
+ {
+ if (createLoggerFactory == null)
+ {
+ throw new ArgumentNullException(nameof(createLoggerFactory));
+ }
+
+ _createLoggerFactoryDelegate = createLoggerFactory;
+ return this;
+ }
+
+ ///
+ /// Adds a delegate for configuring the provided . This may be called multiple times.
+ ///
+ /// The delegate that configures the .
+ /// The .
+ public IWebHostBuilder ConfigureLogging(Action configureLogging) where T : ILoggerFactory
+ {
+ if (configureLogging == null)
+ {
+ throw new ArgumentNullException(nameof(configureLogging));
+ }
+ _configureLoggingDelegates.Add(factory =>
+ {
+ if (factory is T typedFactory)
+ {
+ configureLogging(typedFactory);
+ }
+ });
+ return this;
+ }
+
+ ///
+ /// Adds a delegate for configuring the that will construct an .
+ ///
+ /// The delegate for configuring the that will be used to construct an .
+ /// The .
+ public IWebHostBuilder ConfigureConfiguration(Action configureDelegate)
+ {
+ if (configureDelegate == null)
+ {
+ throw new ArgumentNullException(nameof(configureDelegate));
+ }
+
+ _configureConfigurationBuilderDelegates.Add(configureDelegate);
+ return this;
+ }
+
///
/// Builds the required services and an which hosts a web application.
///
@@ -185,23 +241,35 @@ namespace Microsoft.AspNetCore.Hosting
var appEnvironment = PlatformServices.Default.Application;
var contentRootPath = ResolveContentRootPath(_options.ContentRootPath, appEnvironment.ApplicationBasePath);
var applicationName = _options.ApplicationName ?? appEnvironment.ApplicationName;
+ var hostingContext = new WebHostBuilderContext
+ {
+ Configuration = _config
+ };
// Initialize the hosting environment
_hostingEnvironment.Initialize(applicationName, contentRootPath, _options);
+ hostingContext.HostingEnvironment = _hostingEnvironment;
var services = new ServiceCollection();
services.AddSingleton(_hostingEnvironment);
+ var builder = new ConfigurationBuilder()
+ .SetBasePath(_hostingEnvironment.ContentRootPath)
+ .AddInMemoryCollection(_config.AsEnumerable());
+
+ foreach (var configureConfiguration in _configureConfigurationBuilderDelegates)
+ {
+ configureConfiguration(hostingContext, builder);
+ }
+
+ var configuration = builder.Build();
+ services.AddSingleton(configuration);
+ hostingContext.Configuration = configuration;
+
// The configured ILoggerFactory is added as a singleton here. AddLogging below will not add an additional one.
- if (_loggerFactory == null)
- {
- _loggerFactory = new LoggerFactory();
- services.AddSingleton(provider => _loggerFactory);
- }
- else
- {
- services.AddSingleton(_loggerFactory);
- }
+ var loggerFactory = _createLoggerFactoryDelegate?.Invoke(hostingContext) ?? new LoggerFactory();
+ services.AddSingleton(loggerFactory);
+ hostingContext.LoggerFactory = loggerFactory;
var exceptions = new List();
@@ -236,9 +304,10 @@ namespace Microsoft.AspNetCore.Hosting
}
}
+ // Kept for back-compat, will remove once ConfigureLogging is removed.
foreach (var configureLogging in _configureLoggingDelegates)
{
- configureLogging(_loggerFactory);
+ configureLogging(loggerFactory);
}
//This is required to add ILogger of T.
diff --git a/src/Microsoft.AspNetCore.Hosting/WebHostBuilderExtensions.cs b/src/Microsoft.AspNetCore.Hosting/WebHostBuilderExtensions.cs
index 6e2b7a7552..50a451a331 100644
--- a/src/Microsoft.AspNetCore.Hosting/WebHostBuilderExtensions.cs
+++ b/src/Microsoft.AspNetCore.Hosting/WebHostBuilderExtensions.cs
@@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Hosting
{
@@ -92,5 +93,28 @@ namespace Microsoft.AspNetCore.Hosting
services.Replace(ServiceDescriptor.Singleton>(new DefaultServiceProviderFactory(options)));
});
}
+
+ ///
+ /// Configures and use a for the web host.
+ ///
+ /// The to configure.
+ /// A callback used to configure the that will be added as a singleton and used by the application.
+ /// The .
+ public static IWebHostBuilder UseLoggerFactory(this IWebHostBuilder hostBuilder, Action configure)
+ {
+ if (configure == null)
+ {
+ throw new ArgumentNullException(nameof(configure));
+ }
+
+ hostBuilder.UseLoggerFactory(context =>
+ {
+ var loggerFactory = new LoggerFactory();
+ configure(context, loggerFactory);
+ return loggerFactory;
+ });
+
+ return hostBuilder;
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Hosting.Tests/Fakes/CustomLoggerFactory.cs b/test/Microsoft.AspNetCore.Hosting.Tests/Fakes/CustomLoggerFactory.cs
new file mode 100644
index 0000000000..c09d42a579
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Hosting.Tests/Fakes/CustomLoggerFactory.cs
@@ -0,0 +1,43 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+
+namespace Microsoft.AspNetCore.Hosting.Fakes
+{
+ public class CustomLoggerFactory : ILoggerFactory
+ {
+ public void CustomConfigureMethod() { }
+
+ public void AddProvider(ILoggerProvider provider) { }
+
+ public ILogger CreateLogger(string categoryName) => NullLogger.Instance;
+
+ public void Dispose() { }
+ }
+
+ public static class CustomLoggerFactoryExtensions
+ {
+ public static IWebHostBuilder ConfigureCustomLogger(this IWebHostBuilder builder, Action configureLogger)
+ {
+ builder.UseLoggerFactory(_ => new CustomLoggerFactory());
+ builder.ConfigureLogging(configureLogger);
+ return builder;
+ }
+ }
+
+ public class SubLoggerFactory : CustomLoggerFactory { }
+
+ public class NonSubLoggerFactory : ILoggerFactory
+ {
+ public void CustomConfigureMethod() { }
+
+ public void AddProvider(ILoggerProvider provider) { }
+
+ public ILogger CreateLogger(string categoryName) => NullLogger.Instance;
+
+ public void Dispose() { }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Hosting.Tests/WebHostBuilderTests.cs b/test/Microsoft.AspNetCore.Hosting.Tests/WebHostBuilderTests.cs
index ac5a543479..f8e8a4d89b 100644
--- a/test/Microsoft.AspNetCore.Hosting.Tests/WebHostBuilderTests.cs
+++ b/test/Microsoft.AspNetCore.Hosting.Tests/WebHostBuilderTests.cs
@@ -219,6 +219,140 @@ namespace Microsoft.AspNetCore.Hosting
Assert.Equal(2, callCount);
}
+ [Fact]
+ public void UseLoggerFactoryDelegateIsHonored()
+ {
+ var loggerFactory = new LoggerFactory();
+
+ var hostBuilder = new WebHostBuilder()
+ .UseLoggerFactory(_ => loggerFactory)
+ .UseServer(new TestServer())
+ .UseStartup();
+
+ var host = (WebHost)hostBuilder.Build();
+
+ Assert.Same(loggerFactory, host.Services.GetService());
+ }
+
+ [Fact]
+ public void UseLoggerFactoryFuncAndConfigureLoggingCompose()
+ {
+ var callCount = 0; //Verify that multiple configureLogging calls still compose correctly.
+ var loggerFactory = new LoggerFactory();
+ var hostBuilder = new WebHostBuilder()
+ .UseLoggerFactory(_ => loggerFactory)
+ .ConfigureLogging(factory =>
+ {
+ Assert.Equal(0, callCount++);
+ })
+ .ConfigureLogging(factory =>
+ {
+ Assert.Equal(1, callCount++);
+ })
+ .UseServer(new TestServer())
+ .UseStartup();
+ var host = (WebHost)hostBuilder.Build();
+ Assert.Equal(2, callCount);
+ Assert.Same(loggerFactory, host.Services.GetService());
+ }
+
+ [Fact]
+ public void ConfigureLoggingCalledIfLoggerFactoryTypeMatches()
+ {
+ var callCount = 0;
+ var hostBuilder = new WebHostBuilder()
+ .UseLoggerFactory(_ => new SubLoggerFactory())
+ .ConfigureLogging(factory =>
+ {
+ Assert.Equal(0, callCount++);
+ })
+ .UseServer(new TestServer())
+ .UseStartup();
+
+ var host = (WebHost)hostBuilder.Build();
+ Assert.Equal(1, callCount);
+ }
+
+ [Fact]
+ public void ConfigureLoggingNotCalledIfLoggerFactoryTypeDoesNotMatches()
+ {
+ var callCount = 0;
+ var hostBuilder = new WebHostBuilder()
+ .UseLoggerFactory(_ => new NonSubLoggerFactory())
+ .ConfigureLogging(factory =>
+ {
+ Assert.Equal(0, callCount++);
+ })
+ .UseServer(new TestServer())
+ .UseStartup();
+
+ var host = (WebHost)hostBuilder.Build();
+ Assert.Equal(0, callCount);
+ }
+
+ [Fact]
+ public void CanUseCustomLoggerFactory()
+ {
+ var hostBuilder = new WebHostBuilder()
+ .ConfigureCustomLogger(factory =>
+ {
+ factory.CustomConfigureMethod();
+ })
+ .UseServer(new TestServer())
+ .UseStartup();
+ var host = (WebHost)hostBuilder.Build();
+ Assert.IsType(typeof(CustomLoggerFactory), host.Services.GetService());
+ }
+
+ [Fact]
+ public void ThereIsAlwaysConfiguration()
+ {
+ var hostBuilder = new WebHostBuilder()
+ .UseServer(new TestServer())
+ .UseStartup();
+ var host = (WebHost)hostBuilder.Build();
+
+ Assert.NotNull(host.Services.GetService());
+ }
+
+ [Fact]
+ public void ConfigureConfigurationSettingsPropagated()
+ {
+ var hostBuilder = new WebHostBuilder()
+ .UseSetting("key1", "value1")
+ .ConfigureConfiguration((context, configBuilder) =>
+ {
+ var config = configBuilder.Build();
+ Assert.Equal("value1", config["key1"]);
+ })
+ .UseServer(new TestServer())
+ .UseStartup();
+ var host = (WebHost)hostBuilder.Build();
+ }
+
+ [Fact]
+ public void CanConfigureConfigurationAndRetrieveFromDI()
+ {
+ var hostBuilder = new WebHostBuilder()
+ .ConfigureConfiguration((_, configBuilder) =>
+ {
+ configBuilder
+ .AddInMemoryCollection(
+ new KeyValuePair[]
+ {
+ new KeyValuePair("key1", "value1")
+ })
+ .AddEnvironmentVariables();
+ })
+ .UseServer(new TestServer())
+ .UseStartup();
+ var host = (WebHost)hostBuilder.Build();
+
+ var config = host.Services.GetService();
+ Assert.NotNull(config);
+ Assert.Equal("value1", config["key1"]);
+ }
+
[Fact]
public void DoNotCaptureStartupErrorsByDefault()
{