Reputation: 4297
In asp.net core 1.1 I could inject the IServiceProvider into the logger provider and resolve my logger when CreateLogger
was called, but it all changed in asp.net core 2.0
My ILogger implementation needs dependencies injected. How can I achieve this?
Upvotes: 9
Views: 7268
Reputation: 91
If you are configuring logging in program.cs you can create a function to configure logging and get an instance of logging provider like this:
private static void ConfigureApplicationLogging(WebHostBuilderContext context, ILoggingBuilder loggingBuilder)
{
loggingBuilder.AddConfiguration(context.Configuration.GetSection("Logging"));
loggingBuilder.AddDebug();
loggingBuilder.AddConsole();
var serviceProvider = loggingBuilder.Services.BuildServiceProvider();
loggingBuilder.AddProvider(new DoxErrorLoggerProvider(serviceProvider, null));
}
Then in BuildWebHost you will configure logging as follows:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging(ConfigureApplicationLogging)
.UseNLog()
.UseStartup<Startup>()
.Build();
Upvotes: 1
Reputation: 9811
Starting of with that dependency thing you need in various places
public class SomeDependency : ISomeDependency
{
}
An extension file so we can configure logging on the ServiceCollection as per MSDN Pretty standard stuff you can find on various sources
public static class ApplicationLoggerFactoryExtensions
{
public static ILoggingBuilder CustomLogger(this ILoggingBuilder builder)
{
builder.Services.AddSingleton<ILoggerProvider, CustomLoggerProvider>();
//Be careful here. Singleton may not be OK for multi tenant applications - You can try and use Transient instead.
return builder;
}
}
The logger provider is the part that gets called AFTER services are built when you are working in your business code and need to log stuff.
So in the context of application the DI is built and available here. And it probably makes sense now why ILoggerProvider
exists now.
public class CustomLoggerProvider : ILoggerProvider
{
private ISomeDependency someDependency;
public CustomLoggerProvider(ISomeDependency someDependency)
{
this.someDependency = someDependency;
}
public ILogger CreateLogger(string categoryName)
{
return new CustomeLogger(someDependency);
}
}
The concrete custom logger pretty simple stuff
public class CustomLogger : ILogger
{
public CustomLogger(ISomeDependency dependency)
{
}
}
And in the place where you are configuring your ServiceCollection.. as in the OP's question in Startup.cs
private void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ISomeDependency, SomeDependency>();
//services.AddSingleton<ILogger, CustomLogger>(); <== NO
var loggerFactory = new LoggerFactory(); //??? newer DotNet gives you LoggerFactory in startup this may be unnecessary.
//Add some console printer
services.AddLogging(configure => configure.AddConsole())
.Configure<LoggerFilterOptions>(options => options.MinLevel = LogLevel.Trace);
//Add our custom logger
services.AddLogging(configure => configure.CustomLogger()); // <== our extension helping out!
}
✘ DO NOT - Do not add any ILogger to your services
The whole point of LoggerFactory
and LoggerProvider
configuration is to simplify using ILogger
public MyBusinessService(ILogger<BusinessServiceClass> log)
{
log.Information("Please tell all registered loggers I am logging!);
}
In my example it will print out message to console if available and the CustomLogger that took a Dependency we injected. If you register more.. it will go to all of them
Upvotes: 3
Reputation: 31282
ASP.NET core provides possibility to replace built-in DI container with custom one (see this article for details). You could use this possibility to obtain instance of IServiceProvider
earlier for logging bootstrapping while still using standard .Net core DI container.
To do this you should change return value of Startup.ConfigureServices(IServiceCollection services)
method from void to IServiceProvider
. You can use this possibility to build instance of IServiceProvider in ConfigureServices, use it for logging bootstrapping and then return from the method.
Sample code:
public interface ISomeDependency
{
}
public class SomeDependency : ISomeDependency
{
}
public class CustomLogger : ILogger
{
public CustomLogger(ISomeDependency dependency)
{
}
// ...
}
public class CustomLoggerProvider : ILoggerProvider
{
private readonly IServiceProvider serviceProvider;
public CustomLoggerProvider(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
public ILogger CreateLogger(string categoryName)
{
return serviceProvider.GetRequiredService<ILogger>();
}
// ...
}
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
return ConfigureLogging(services);
}
private IServiceProvider ConfigureLogging(IServiceCollection services)
{
services.AddTransient<ISomeDependency, SomeDependency>();
services.AddSingleton<ILogger, CustomLogger>();
IServiceProvider serviceProvider = services.BuildServiceProvider();
var loggerFactory = new LoggerFactory();
loggerFactory.AddProvider(new CustomLoggerProvider(serviceProvider));
return serviceProvider;
}
// ...
}
Upvotes: 8