Простой инжектор: зарегистрируйте 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,

Надеюсь, поможет!

Другие вопросы по тегам