繁体   English   中英

简单注入器:注册 ILogger<T> 通过使用 ILoggerFactory.CreateLogger<T> ()

[英]Simple Injector: Register ILogger<T> by using ILoggerFactory.CreateLogger<T>()

我正在使用一个使用 Simple Injector 作为依赖注入器的项目。 另一方面,该项目使用 Microsoft.Extensions.Logging 来记录某些类中发生的事件。

我的技术问题很容易解释。 我想在我的 DI 中注册独立于被调用的类 T 的 ILogger,但确实需要从我的ILoggerFactory.CreateLogger<T>()方法中完成它,因为这使用Microsoft.Extensions.Configuration获取记录器配置。

我需要使用这样的东西来实例我的记录器:

private Microsoft.Extensions.Logging.ILogger CreateLogger<T>()
{
     var factory = this.ResolveService<ILoggerFactory>();
     var logger = factory.CreateLogger<T>();
     return logger;
}

我可以通过执行以下操作来实现注入:

Container.Register(typeof(ILogger<>), typeof(Logger<>));

这使我们能够解决以下问题:

public class SomeApiController : ApiController
{
     public SomeApiController(ILogger<SomeApiController> logger)
     {
         //logger is well instantiated, but doesn't got the configuration
         logger.LogInformation("test log.");
     }
}

但正如我所说,这样做没有通过从Microsoft.Extensions.Logging.ILoggerFactory类获得的配置,所以这没有用。

有没有办法使用我的CreateLogger<T>注册ILogger<T> CreateLogger<T>

使用以下注册:

container.RegisterInstance<ILoggerFactory>(loggerFactory);
container.RegisterSingleton(typeof(ILogger<>), typeof(Logger<>));

或者,如果您要将Simple Injector 集成到通用主机或 ASP.NET Core 应用程序中,请使用.AddLogging()扩展方法甚至将非通用ILogger注入您的应用程序组件,如本 ASP.NET 中所示核心Startup类:

public class Startup
{
    ...

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddLogging(); // Adds logging to the framework

        // AddSimpleInjector enables "cross wiring," which means you can let
        // Simple Injector-resolved components to depend on the generic
        // ILogger<T> abstraction.
        services.AddSimpleInjector(container, options =>
        {
            options.AddAspNetCore();
            
            // AddLogger allows Simple Injector-resolved components to depend on 
            // the non-generic Microsoft.Extensions.Logging.ILogger interface.
            // Simple Injector will automatically inject the correct ILogger<T>
            // for you.
            options.AddLogging();
        });
    }

    ...
}

有关完整示例,请参阅ASP.NET Core 和 ASP.NET Core MVC 集成指南

让应用程序组件依赖ILogger而不是ILogger<T> ,可以使您的代码更简单、更易于测试且不易出错。 如果您使用没有服务集合集成的Simple Injector(如前面的示例所示,您可以使用以下注册让 Simple Injector 确保在注入ILogger时仍然注入正确的Logger<T>

container.RegisterConditional(
    typeof(ILogger),
    c => typeof(Logger<>).MakeGenericType(c.Consumer.ImplementationType),
    Lifestyle.Singleton,
    _ => true);

这确保每个应用程序组件都有自己的Logger<T>实例,其中T是记录器注入的组件的类型。 以以下依赖ILogger类为例:

public class ComponentA : IService
{
    public ComponentA(ILogger logger) { ... }
}

上述注册将确保ComponentA注入Logger<ComponentA> ,即使它仅依赖于ILogger而不是ILogger<T>

如果以上内容满足您的需求,您可以在此处停止阅读……或者如果您对更可靠的解决方案感兴趣,请继续阅读。

可靠的解决方案

除了让应用程序组件依赖于框架定义的ILogger抽象之外,您还可以选择定义特定于应用程序的记录器抽象,如依赖倒置原则(DIP) 所规定的那样。

DIP 声明抽象应该由应用程序本身定义 - 这意味着您定义自己的记录器抽象(另请参阅this以了解您为什么要这样做)并在此基础上构建一个适配器,很像这里描述的那样. 您可以简单地从所描述的MicrosoftLoggingAdapter派生您的通用适配器,如下所示:

public sealed class MicrosoftLoggingAdapter<T> : MicrosoftLoggingAdapter 
{
    public MicrosoftLoggingAdapter(ILoggerFactory factory) 
        : base(factory.CreateLogger<T>()) { }
}

使用这个通用适配器,您可以按如下方式配置 Simple Injector:

container.RegisterInstance<ILoggerFactory>(factory);

container.RegisterConditional(
    typeof(MyApplication.Abstractions.ILogger),
    c => typeof(MicrosoftLoggingAdapter<>).MakeGenericType(c.Consumer.ImplementationType),
    Lifestyle.Singleton,
    _ => true);

基于史蒂文的解决方案,我发布了我的答案以帮助其他人:

    private void RegisterServices()
    {
        Container.Register(ConfigureLogger, Lifestyle.Singleton);            
        Container.Register(typeof(ILogger<>), typeof(LoggingAdapter<>));
    }

    private ILoggerFactory ConfigureLogger()
    {
        LoggerFactory factory = new LoggerFactory();

        var config = new ConfigurationBuilder()
            .AddJsonFile("logging.json")
            .Build();

        //serilog provider configuration
        var log = new LoggerConfiguration()
                 //.ReadFrom.Configuration(config)
                 .WriteTo
                 .RollingFile(ConfigSettings.LogsPath)
                 .CreateLogger();

        factory.AddSerilog(log);

        return factory;
    }

    public class LoggingAdapter<T> : ILogger<T>
    {
        private readonly Microsoft.Extensions.Logging.ILogger adaptee;          

        public LoggingAdapter(ILoggerFactory factory)
        {
            adaptee = factory.CreateLogger<T>();
        }

        public IDisposable BeginScope<TState>(TState state)
        {
            return adaptee.BeginScope(state);
        }

        public bool IsEnabled(LogLevel logLevel)
        {
            return adaptee.IsEnabled(logLevel);
        }

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            adaptee.Log(logLevel, eventId, state, exception, formatter);
        }
    }   

如您所见,我的解决方案使用 Serilog 作为提供程序来登录Microsoft.Extensions.Logging

希望能帮助到你!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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