简体   繁体   中英

How can I inject dependencies into a custom ILogger in asp.net core 2.0?

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?

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;
    }

    //  ...
}

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();

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!
}

So just a note for usage of ILogger

✘ 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM