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

泄露秘密 提交于 2019-11-30 12:11:55

问题


I'm working with a project which utilizes Simple Injector as dependency injector. On the other hand, this project uses Microsoft.Extensions.Logging in order to log the events that occurs in certain classes.

My technical issue is pretty simple to explain. I want to register in my DI the ILogger independently of the class T which is being invoked, but I DO NEED to do it from my ILoggerFactory.CreateLogger<T>() method because this gets the logger configuration using Microsoft.Extensions.Configuration.

I need to use something like this in order to instance my logger:

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

I could achieve the injection by doing:

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

And this allows us to resolve something like:

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

But as I said, this does it without passing through the configuration obtained from the Microsoft.Extensions.Logging.ILoggerFactory class, so this isn't useful.

Is there a way to register ILogger<T> by using my CreateLogger<T>?


回答1:


TLDR; Use the following registrations:

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

The CreateLogger<T>() method is an extension method over ILoggerFactory and is implemented (by LoggerFactoryExtensions) as follows:

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

CreateLogger<T> merely creates a new Logger<T> while supplying the factory. In other words, calling CreateLogger<T> would have the same effect as creating a Logger<T> manually, or letting Simple Injector do it.

Therefore, the given registration should solve your question:

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

But instead of letting application components depend on ILogger<T> dependencies, it is better to let them depend on ILogger instead. This makes your code simpler, easier to test, and less error prone. You can let Simple Injector ensure the correct Logger<T> is still injected:

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

This ensures that every application component gets its own Logger<T> instance, where T is the type of the component the logger is injected into. Take the following class for example that depends on ILogger:

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

The above registration will ensure that ComponentA is injected with a Logger<ComponentA>, even though it simply depends on ILogger and not on ILogger<T>.

You can stop reading here, or continue reading if you're interested in a more SOLID solution.

An even better solution

Instead of letting application components depend on the framework-defined ILogger abstraction, you could also choose to define an application-specific logger abstraction, as prescribed by the Dependency Inversion Principle (DIP).

The DIP states that abstractions should be defined by the application itself—this means you define your own logger abstraction and on top of that you build an adapter, much like described here. You can simply derive your generic adapter from the described MicrosoftLoggingAdapter as follows:

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

Using this generic adapter, you can configure Simple Injector as follows:

container.RegisterInstance<ILoggerFactory>(factory);

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



回答2:


Based on Steven's solution, I post my answer to help anyone else:

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

As you can see, my solution is using Serilog as a provider for logging in Microsoft.Extensions.Logging.

Hope it helps!



来源:https://stackoverflow.com/questions/41243485/simple-injector-register-iloggert-by-using-iloggerfactory-createloggert

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!