web-dev-qa-db-ja.com

シンプルインジェクター:ILoggerFactory.CreateLogger <T>()を使用してILogger <T>を登録します

SimpleInjectorを依存性注入器として利用するプロジェクトに取り組んでいます。一方、このプロジェクトでは、特定のクラスで発生するイベントをログに記録するためにMicrosoft.Extensions.Loggingを使用します。

私の技術的な問題は非常に簡単に説明できます。呼び出されているクラスTとは無関係にILoggerをDIに登録したいのですが、I DO NEEDILoggerFactory.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>を登録する方法はありますか?

13
Mauro Bilotti

[〜#〜] tldr [〜#〜];次の登録を使用します。

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

CreateLogger<T>()メソッドはILoggerFactoryの拡張メソッドであり、次のように実装されます( LoggerFactoryExtensions によって)。

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

_CreateLogger<T>_は、工場への供給中に新しい_Logger<T>_を作成するだけです。つまり、_CreateLogger<T>_を呼び出すと、_Logger<T>_を手動で作成するか、SimpleInjectorに実行させるのと同じ効果があります。

したがって、与えられた登録はあなたの質問を解決するはずです:

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

ただし、アプリケーションコンポーネントを_ILogger<T>_の依存関係に依存させるのではなく、代わりにILoggerに依存させることをお勧めします。これにより、コードが単純になり、テストが容易になり、エラーが発生しにくくなります。 Simple Injectorに、正しい_Logger<T>_がまだ注入されていることを確認させることができます。

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

これにより、すべてのアプリケーションコンポーネントが独自の_Logger<T>_インスタンスを取得します。ここで、Tはロガーが挿入されるコンポーネントのタイプです。 ILoggerに依存する次のクラスを例にとってみましょう。

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

上記の登録により、ComponentAに依存し、_Logger<ComponentA>_には依存しない場合でも、ILoggerに_ILogger<T>_が注入されます。

ここで読むのをやめるか、もっとSOLIDソリューションに興味があるなら、読み続けることができます。

さらに優れたソリューション

アプリケーションコンポーネントをフレームワークで定義されたILogger抽象化に依存させる代わりに、 依存性逆転の原則 (DIP)で規定されているように、アプリケーション固有のロガー抽象化を定義することもできます。

DIPは、抽象化はアプリケーション自体によって定義される必要があると述べています。つまり、独自の logger抽象化 を定義し、その上に、説明されているように ここ のようにアダプターを構築します。次のように、説明されているMicrosoftLoggingAdapterから汎用アダプターを簡単に導出できます。

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

この汎用アダプターを使用して、SimpleInjectorを次のように構成できます。

_container.RegisterInstance<ILoggerFactory>(factory);

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

スティーブンの解決策に基づいて、私は他の誰かを助けるために私の答えを投稿します:

    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);
        }
    }   

ご覧のとおり、私のソリューションはMicrosoft.Extensions.LoggingにログインするためのプロバイダーとしてSerilogを使用しています。

それが役に立てば幸い!

12
Mauro Bilotti