Простой инжектор: зарегистрируйте ILogger путем использование ILoggerFactory.Креатологгер ()

Я работаю с проектом, который использует простой инжектор инжектор зависимостей. С другой стороны, этот проект использует Microsoft.Увеличение.Ведение журнала для регистрации событий, происходящих в определенных классах.

моя техническая проблема довольно проста в объяснении. Я хочу зарегистрировать в своем 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> С помощью my CreateLogger<T>?

2 ответов


способ сделать это с помощью простого инжектора-указать общий прокси-класс, который делегирует вызов ILoggerFactory.

это, однако, вызывает проблему при использовании Microsoft.Увеличение.Ведение журнала, потому что это ILogger<T> абстракция-это одна большая Принцип Разделения Интерфейса нарушение. Нарушения ISP проблематичны, потому что они затрудняют создание (среди прочего) прокси-классов. Но вы должны воздерживаться от использования этой абстракции непосредственно в ваши компоненты приложения любым способом, как предписано Принцип Инверсии Зависимостей.

поскольку абстракции должны определяться самим приложением, это в основном означает, что вам нужно определить свой собственный абстракция регистратор и поверх этого вы строите адаптер, как описано здесь. Вы можете просто вывести свой универсальный адаптер из описанного MicrosoftLoggingAdapter следующим образом:

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

используя этот универсальный адаптер, вы можете настроить простой инжектор следующим образом:

container.RegisterSingleton<ILoggerFactory>(factory);

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

в случае, если создание собственной абстракции не является (пока) вариантом, ваш второй лучший выбор-переопределить поведение инъекции зависимостей простого инжектора со следующей Microsoft.Увеличение.Класс ведения журнала:

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.

надеюсь, что это помогает!