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

时间:2016-12-20 13:27:33

标签: c# dependency-injection simple-injector ms-extensions-logging

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

我的技术问题很容易解释。 我想在我的DI中注册ILogger独立于正在调用的类T,但是我需要从我的ILoggerFactory.CreateLogger<T>()方法执行它,因为这使用{{{}获取记录器配置1}}。

我需要使用类似的东西来实例化我的记录器:

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注册ILogger<T>

2 个答案:

答案 0 :(得分:18)

<强> TLDR ;使用以下注册:

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

CreateLogger<T>()方法是ILoggerFactory上的扩展方法,并按以下方式实施(LoggerFactoryExtensions):

public static ILogger<T> CreateLogger<T>(this ILoggerFactory factory)
{
    return new Logger<T>(factory);
}

CreateLogger<T>仅在为工厂供应时创建新的Logger<T>。换句话说,调用CreateLogger<T>与手动创建Logger<T>或让Simple Injector执行此操作具有相同的效果。

因此,给定的注册应解决您的问题:

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

但是,不要让应用程序组件依赖ILogger<T>依赖项,而是让它们依赖于ILogger。这使您的代码更简单,更容易测试,并且更不容易出错。您可以让Simple Injector确保仍然注入正确的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抽象,而不是Dependency Inversion Principle(DIP)规定的。

DIP声明抽象应该由应用程序本身定义 - 这意味着您定义自己的logger abstraction,并在此基础上构建适配器,就像描​​述here一样。您可以简单地从描述的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);

答案 1 :(得分:9)

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

    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的提供程序。

希望它有所帮助!