Simple injector: register ILogger <T> using ILoggerFactory.CreateLogger <T> ()

I am working with a project that uses Simple Injector as a dependency injector. On the other hand, this project uses Microsoft.Extensions.Logging to log events that occur in specific classes.

My technical problem is pretty simple to explain. I want to register ILogger in my DI regardless of the class T that is being called, but I DO NOT NEED to do this from my ILoggerFactory.CreateLogger<T>() method, because it gets the log configuration using Microsoft.Extensions.Configuration .

I need to use something like this for an instance of my registrar:

 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 solve 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 this without going through the configuration retrieved from the Microsoft.Extensions.Logging.ILoggerFactory class, so this is not useful.

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

+9
source share
2 answers

TL; DR ; Use the following registrations:

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

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

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

CreateLogger<T> simply creates a new Logger<T> when the factory CreateLogger<T> . In other words, calling CreateLogger<T> will have the same effect as creating Logger<T> manually or letting Simple Injector do this.

Therefore, this 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’s better to let them depend on ILogger . This makes your code simpler, easier to test, and less error prone. You can enable Simple Injector to ensure that the correct Logger<T> is still entered:

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

This ensures that each component of the application receives its own instance of Logger<T> , where T is the type of component into which the registrar is entered. Take the following class, for example, which depends on ILogger :

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

The above registration ensures that Logger<ComponentA> will be introduced in ComponentA , even if it simply depends on ILogger and not ILogger<T> .

You can stop reading here or continue reading if you are interested in a more solid solution.

Even better solution

Instead of allowing the application components to depend on the ILogger abstraction defined in ILogger , you can also define the registrar abstraction for a particular application, as prescribed by the Dependency Inversion Principle (DIP).

DIP states that abstractions must be defined by the application itself - this means that you define your own abstraction of the logger and, in addition, you build the adapter as described here . You can simply extract your universal adapter from the described MicrosoftLoggingAdapter as follows:

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

Using this universal 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); 
+17
source

Based on Stephen's decision, 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 uses Serilog as a provider to log into Microsoft.Extensions.Logging .

Hope this helps!

+8
source

Source: https://habr.com/ru/post/1261572/


All Articles