如何在asp.net core 2.0中向自定义ILogger注入依赖项?

8
在asp.net core 1.1中,我可以将IServiceProvider注入到记录器提供程序中,并在调用CreateLogger时解析我的记录器,但在asp.net core 2.0中一切都改变了。
我的ILogger实现需要依赖注入。我该如何实现?
3个回答

8
ASP.NET Core提供了替换内置DI容器的可能性,可以使用自定义容器。详情请参见文章。您可以利用这个可能性,在仍使用标准 .Net core DI容器的同时,在启动引导期间更早地获取IServiceProvider实例。
要做到这一点,您应该将Startup.ConfigureServices(IServiceCollection services)方法的返回值从void更改为IServiceProvider。您可以利用这个可能性在ConfigureServices中构建IServiceProvider实例,并用它来记录启动引导,然后从该方法返回。
示例代码:
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;
    }

    //  ...
}

2
但我的问题是ConfigureLogging在ConfigureServices之前被调用,因此依赖项尚未注册。 - Clement
1
我指的是ConfigureLogging扩展方法,如此处所示https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?tabs=aspnetcore2x - Clement
ConfigureLogging()只是一个帮助扩展方法,它在适当的时间调用您提供的lambda表达式。由于您的特定需求,您不能使用ConfigureLogging,因为它对您的情况不够灵活。 - CodeFuller
如果你只有一些基本应用程序,那就没问题,但对于复杂/租户/消息型系统来说就不行了。正如Clement所说的那样,容器是在Startup之外构建的(或者Startup甚至不存在)。你现在所做的只是在传递ServiceCollection并手动获取Service……别误会我的意思……但我认为这不能算作依赖注入,因为你没有倒置依赖,并且只是创建了不必要的耦合复杂性。看看我的答案……我想了一会儿,但是在你的答案中我解决了其中的一部分! - Piotr Kula

1

首先需要讲一下在不同场合中所需的依赖项

public class SomeDependency : ISomeDependency
{
}

一个扩展文件,使我们可以按照MSDN上的方式配置ServiceCollection的日志记录。 这是一些常见的内容,您可以在各种来源中找到。
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;
    }
}

记录器提供程序是在构建服务后被调用的部分,当您在业务代码中工作并需要记录某些内容时使用。
因此,在应用程序上下文中,DI已经构建并可用于此处。现在 ILoggerProvider 的存在可能就有意义了。
public class CustomLoggerProvider : ILoggerProvider
{
    private ISomeDependency someDependency;

    public CustomLoggerProvider(ISomeDependency someDependency)
    {
        this.someDependency = someDependency;
    }

    public ILogger CreateLogger(string categoryName)
    {
        return new CustomeLogger(someDependency);
    }
}

具体的自定义记录器非常简单的东西。
public class CustomLogger : ILogger
{
    public CustomLogger(ISomeDependency dependency)
    {
    }
}

在你配置ServiceCollection的地方...就像OP在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!
}

使用ILogger时的注意事项

✘ 不要 - 不要将任何ILogger添加到您的服务中

LoggerFactoryLoggerProvider配置的整个目的是简化使用ILogger

public MyBusinessService(ILogger<BusinessServiceClass> log)
{
  log.Information("Please tell all registered loggers I am logging!);
}

在我的例子中,如果可用,它将向控制台打印消息和我们注入的依赖项 CustomLogger。如果您注册更多内容...它将进入所有这些内容。

1
如果您正在配置program.cs中的日志记录,您可以创建一个函数来配置日志记录并获取日志提供程序的实例,如下所示:
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));
    }

然后在BuildWebHost中,您将按以下方式配置日志记录:

        public static IWebHost BuildWebHost(string[] args) =>            
        WebHost.CreateDefaultBuilder(args)
            .ConfigureLogging(ConfigureApplicationLogging)
            .UseNLog()
            .UseStartup<Startup>()
            .Build();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接