Простой инжектор: зарегистрируйте ILogger<T> с помощью ILoggerFactory.CreateLogger<T>()
Я работаю с проектом, который использует Simple Injector в качестве инжектора зависимости. С другой стороны, этот проект использует Microsoft.Extensions.Logging для регистрации событий, которые происходят в определенных классах.
Моя техническая проблема довольно проста для объяснения. Я хочу зарегистрировать в моем DI ILogger независимо от класса T, который вызывается, но мне НУЖНО сделать это из моего 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
класс, так что это не полезно.
Есть ли способ зарегистрироваться
ILogger<T>
используя мойCreateLogger<T>
?
2 ответа
Способ сделать это с помощью Simple Injector, указав универсальный прокси-класс, который делегирует вызов ILoggerFactory
,
Это, однако, вызывает проблему при использовании Microsoft.Extensions.Logging, потому что это ILogger<T>
абстракция - одно большое нарушение Принципа Разделения Интерфейса. Нарушения интернет-провайдера проблематичны, потому что они усложняют создание (среди прочего) прокси-классов. Но вы должны воздерживаться от использования этой абстракции непосредственно в ваших прикладных компонентах любым способом, как это предписано Принципом инверсии зависимости.
Поскольку абстракции должны быть определены самим приложением, это в основном означает, что вам нужно определить собственную абстракцию логгера и, кроме того, вы создадите адаптер, как описано здесь. Вы можете просто получить свой универсальный адаптер из описанного MicrosoftLoggingAdapter
следующее:
public sealed class MicrosoftLoggingAdapter<T> : MicrosoftLoggingAdapter
{
public MicrosoftLoggingAdapter(ILoggerFactory factory)
: base(factory.CreateLogger<T>()) { }
}
Используя этот универсальный адаптер, вы можете настроить Simple Injector следующим образом:
container.RegisterSingleton<ILoggerFactory>(factory);
container.RegisterConditional(
typeof(MyApplication.Abstractions.ILogger),
c => typeof(MicrosoftLoggingAdapter<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
c => true);
В случае, если создание собственной абстракции (пока) не вариант, ваш второй лучший вариант - переопределить поведение внедрения зависимостей Simple Injector следующим классом Microsoft.Extensions.Logging:
class MsContextualLoggerInjectionBehavior : IDependencyInjectionBehavior
{
private readonly ILoggerFactory factory;
private readonly IDependencyInjectionBehavior original;
private readonly Container container;
public MsContextualLoggerInjectionBehavior(
ILoggerFactory factory, Container container)
{
this.factory = factory;
this.original = container.Options.DependencyInjectionBehavior;
this.container = container;
}
public void Verify(InjectionConsumerInfo consumer) => original.Verify(consumer);
public InstanceProducer GetInstanceProducer(InjectionConsumerInfo i, bool t) =>
i.Target.TargetType == typeof(ILogger)
? GetLoggerInstanceProducer(i.ImplementationType)
: original.GetInstanceProducer(i, t);
private InstanceProducer<ILogger> GetLoggerInstanceProducer(Type type) =>
Lifestyle.Singleton.CreateProducer(() => factory.CreateLogger(type), container);
}
Вы можете заменить исходное поведение следующим образом:
container.Options.DependencyInjectionBehavior =
new MsContextualLoggerInjectionBehavior(loggingFactory, container);
Основываясь на решении Стивена, я публикую свой ответ, чтобы помочь кому-либо еще:
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
,
Надеюсь, поможет!