Inyector simple: registre ILogger<T> usando ILoggerFactory.CreateLogger<T>()
Estoy trabajando con un proyecto que utiliza Simple Injector como inyector de dependencia. Por otro lado, este proyecto utiliza Microsoft.Extensions.Logging para registrar los eventos que ocurren en ciertas clases.
Mi problema técnico es bastante sencillo de explicar. Quiero registrar en mi DI el ILogger independientemente de la clase T que se esté invocando, pero NECESITO hacerlo desde mi ILoggerFactory.CreateLogger<T>()
método porque este obtiene la configuración del registrador usando Microsoft.Extensions.Configuration
.
Necesito usar algo como esto para crear una instancia de mi registrador:
private Microsoft.Extensions.Logging.ILogger CreateLogger<T>()
{
var factory = this.ResolveService<ILoggerFactory>();
var logger = factory.CreateLogger<T>();
return logger;
}
Podría lograr la inyección haciendo:
Container.Register(typeof(ILogger<>), typeof(Logger<>));
Y esto nos permite resolver algo como:
public class SomeApiController : ApiController
{
public SomeApiController(ILogger<SomeApiController> logger)
{
//logger is well instantiated, but doesn't got the configuration
logger.LogInformation("test log.");
}
}
Pero como dije, esto lo hace sin pasar por la configuración obtenida de la Microsoft.Extensions.Logging.ILoggerFactory
clase, por lo que no es útil.
¿ Hay alguna manera de registrarse ILogger<T>
usando mi CreateLogger<T>
?
Utilice los siguientes registros:
container.RegisterInstance<ILoggerFactory>(loggerFactory);
container.RegisterSingleton(typeof(ILogger<>), typeof(Logger<>));
O, en caso de que esté integrando Simple Injector en un host genérico o una aplicación ASP.NET Core, utilice el método de extensión .AddLogging() para incluso inyectar un componente no genérico ILogger
en los componentes de su aplicación, como se demuestra en este ASP.NET Core. Startup
clase:
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();
});
}
...
}
Para ver un ejemplo completo, consulte la Guía de integración de ASP.NET Core y ASP.NET Core MVC .
Permitir que los componentes de la aplicación dependan de ILogger
en lugar de ILogger<T>
, hace que su código sea más simple, más fácil de probar y menos propenso a errores. Si está utilizando Simple Injector sin integración de Service Collection (como se mostró en el ejemplo anterior, puede usar el siguiente registro para permitir que Simple Injector se asegure de que Logger<T>
se siga inyectando lo correcto cada vez que ILogger
se inyecta:
container.RegisterConditional(
typeof(ILogger),
c => typeof(Logger<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
_ => true);
Esto garantiza que cada componente de la aplicación obtenga su propia Logger<T>
instancia, donde T
está el tipo de componente en el que se inyecta el registrador. Tomemos como ejemplo la siguiente clase que depende de ILogger
:
public class ComponentA : IService
{
public ComponentA(ILogger logger) { ... }
}
El registro anterior garantizará que ComponentA
se inyecte un Logger<ComponentA>
, aunque simplemente dependa de ILogger
y no de ILogger<T>
.
Puedes dejar de leer aquí si lo anterior se adapta a tus necesidades... o continuar leyendo si estás interesado en una solución más SÓLIDA.
Una solución SÓLIDA
En lugar de permitir que los componentes de la aplicación dependan de la ILogger
abstracción definida por el marco, también puede optar por definir una abstracción de registrador específica de la aplicación, según lo prescrito por el Principio de inversión de dependencia (DIP).
El DIP establece que las abstracciones deben ser definidas por la propia aplicación; esto significa que usted define su propia abstracción del registrador (consulte también esto para obtener una explicación de por qué desea hacer esto) y, además, construye un adaptador, muy parecido a lo que se describe aquí. . Simplemente puede derivar su adaptador genérico a partir de lo que se describe MicrosoftLoggingAdapter
a continuación:
public sealed class MicrosoftLoggingAdapter<T> : MicrosoftLoggingAdapter
{
public MicrosoftLoggingAdapter(ILoggerFactory factory)
: base(factory.CreateLogger<T>()) { }
}
Con este adaptador genérico, puede configurar Simple Injector de la siguiente manera:
container.RegisterInstance<ILoggerFactory>(factory);
container.RegisterConditional(
typeof(MyApplication.Abstractions.ILogger),
c => typeof(MicrosoftLoggingAdapter<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
_ => true);
Basado en la solución de Steven, publico mi respuesta para ayudar a cualquier otra persona:
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);
}
}
Como puede ver, mi solución es utilizar Serilog como proveedor para iniciar sesión Microsoft.Extensions.Logging
.
¡Espero eso ayude!