これは、 AspNet CoreのコンストラクターにILoggerまたはILoggerFactoryを渡す? に多少関連している可能性がありますが、これは特にLibrary Designについてです。それらのライブラリを使用する実際のアプリケーションがロギングを実装する方法についてではありません。
Nugetを介してインストールされる.net標準2.0ライブラリを作成し、そのライブラリを使用するユーザーがデバッグ情報を取得できるようにするために、 Microsoft.Extensions.Logging.Abstractions に依存して許可しています注入される標準化されたロガー。
ただし、複数のインターフェイスが表示され、Web上のサンプルコードではILoggerFactory
を使用して、クラスのctorにロガーを作成することがあります。 Factoryの読み取り専用バージョンのように見えるILoggerProvider
もありますが、実装は両方のインターフェイスを実装する場合としない場合があるため、選択する必要があります。 (工場はプロバイダーよりも一般的です)。
私が見たコードの中には、非ジェネリックILogger
インターフェースを使用し、同じロガーの1つのインスタンスを共有するものもあります。また、一部のコードでILogger<T>
を受け取り、DIコンテナがオープンジェネリック型をサポートすることを期待していますまたは私のライブラリが使用するすべてのILogger<T>
バリエーションの明示的な登録。
現時点では、ILogger<T>
が正しいアプローチであり、おそらくその引数をとらず、代わりにNull Loggerを渡すだけのctorであると思います。こうすることで、ロギングが不要な場合、何も使用されません。ただし、一部のDIコンテナは最大のctorを選択するため、いずれにしても失敗します。
想定ユーザーの頭痛を最小限に抑えながら、必要に応じて適切なロギングサポートを許可するためにここでやることに興味があります。
ILogger
、ILoggerProvider
およびILoggerFactory
の3つのインターフェースがあります。責任を見つけるために ソースコード を見てみましょう:
ILogger
:主な役割は、指定されたログレベルのログメッセージを書き込むことです。
ILoggerProvider
:責任は1つだけです。それはILogger
を作成することです。
ILoggerFactory
:ILogger
の作成とILoggerProvider
の追加の2つの役割があります。
1つ以上のプロバイダ(コンソール、ファイルなど)をファクトリに登録できることに注意してください。ファクトリを使用してロガーを作成すると、登録されているすべてのプロバイダを使用して対応するロガーが作成されます。
ILoggerFactory factory = new LoggerFactory().AddConsole(); // add console provider
factory.AddProvider(new LoggerFileProvider("c:\\log.txt")); // add file provider
Logger logger = factory.CreateLogger(); // <-- creates a console logger and a file logger
したがって、Loggerはロガーのリストを管理しており、ログメッセージをそれらすべてに書き込みます。 Loggerのソースコードを見るとLogger
がILoggers
の配列(つまりLoggerInformation [])を持ち、同時にILogger
インターフェースを実装していることを確認できます。
MSのドキュメント は、ロガーを注入するための2つの方法を提供します。
1。工場への注入:
public TodoController(ITodoRepository todoRepository, ILoggerFactory logger) { _todoRepository = todoRepository; _logger = logger.CreateLogger("TodoApi.Controllers.TodoController"); }
は、Category = TodoApi.Controllers.TodoControllerのLoggerを作成します。
2。一般的な
ILogger<T>
を注入する:public TodoController(ITodoRepository todoRepository, ILogger<TodoController> logger) { _todoRepository = todoRepository; _logger = logger; }
は、Category = Tの完全修飾タイプ名でロガーを作成します。
私の意見では、ドキュメンテーションを混乱させているのは、非ジェネリックのILogger
をインジェクトすることについては何も言及していないということです。上記の同じ例では、非総称のITodoRepository
をインジェクトしていますが、それでもILogger
に対して同じことをしていない理由は説明されていません。
によると Mark Seemann :
インジェクションコンストラクタは依存関係を受け取る以上のことをしてはいけません。
工場にコントローラを投入するのは良い方法ではありません。ロガーを初期化するのはコントローラの責任ではないからです(SRPの違反)。同時に、一般的なILogger<T>
を注入すると、不要なノイズが追加されます。 Stevenの投稿を参照してください Simple Injector blog
(少なくとも上記の記事によると)注入されるべきものは非汎用のILogger
ですが、それでは、それはMicrosoftの組み込みDIコンテナができることではないので、あなたはサードパーティのDIライブラリを使用する必要があります。
これは 別の記事 Nikola Malovicによる、彼はIoCの5つの法則を説明しています。
ニコラのIoCの第4法則
解決されているクラスのすべてのコンストラクタは、独自の依存関係のセットを受け入れる以外には実装をしてはいけません。
ILoggerProvider以外はすべて有効です。 ILoggerとILogger <T>はあなたがロギングに使うべきものです。 ILoggerを取得するには、ILoggerFactoryを使用します。 ILogger <T>は特定のカテゴリのロガーを取得するためのショートカットです(カテゴリとしてのタイプへのショートカット)。
ILoggerを使用してロギングを実行すると、登録されている各ILoggerProvidersにそのログメッセージを処理する機会が与えられます。 ILoggerProviderに直接呼び出すコードを消費することは実際には無効です。
ILogger<T>
はDIのために作られた実際のものです。 ILoggerは、asp.netコアで最も賢い決定の1つだったDIおよびFactoryロジックをすべて自分で作成する代わりに、ファクトリーパターンをはるかに簡単に実装できるようにするために生まれました。
次の中から選択できます。
あなたのコードでファクトリとDIパターンを使う必要があるならILogger<T>
あるいはあなたはDIを必要とせずに簡単なロギングを実装するためにILogger
を使うことができます。
ILoggerProvider
は、登録された各ログのメッセージを処理するための単なるブリッジです。それはあなたがコードに介在するべきである何にも影響を及ぼさないのでそれを使用する必要はありません。それは登録されたILoggerProviderを監視しメッセージを処理します。それはそれについてです。
図書館デザインのための良いアプローチは次のようになります。
1.消費者があなたのクラスにロガーを注入することを強制しないでください。 NullLoggerFactoryを渡して別のコントロールを作成するだけです。
class MyClass
{
private readonly ILoggerFactory _loggerFactory;
public MyClass():this(NullLoggerFactory.Instance)
{
}
public MyClass(ILoggerFactory loggerFactory)
{
this._loggerFactory = loggerFactory ?? NullLoggerFactory.Instance;
}
}
2.ロガーを作成するときに使用するカテゴリの数を制限して、コンシューマがログフィルタリングを簡単に設定できるようにします。 this._loggerFactory.CreateLogger(Consts.CategoryName)
質問にこだわり、私はILogger<T>
が他のオプションの欠点を考慮して、正しいオプションであると思います:
ILoggerFactory
を注入すると、ユーザーはクラスライブラリに可変グローバルロガーファクトリの制御権を放棄することになります。さらに、ILoggerFactory
を受け入れることで、クラスはCreateLogger
メソッドを使って任意のカテゴリ名でログに書き込むことができます。 ILoggerFactory
は通常DIコンテナ内のシングルトンとして利用可能ですが、私はユーザとして、どのライブラリがそれを使う必要があるのか疑問に思います。ILoggerProvider.CreateLogger
はそのように見えますが、インジェクションを目的としていません。これはILoggerFactory.AddProvider
と共に使用されるため、ファクトリは各登録プロバイダから作成された複数のILogger
に書き込む集約ILogger
を作成できます。これを調べると明らかです LoggerFactory.CreateLogger
の実装ILogger
を受け入れることもやり方のように見えますが、.NET Core DIでは不可能です。これは実際に最初にILogger<T>
を提供する必要がある理由のように思えます。つまり、これらのクラスから選択するのであれば、ILogger<T>
よりも良い選択はありません。
別のアプローチは、非ジェネリックのILogger
をラップする何か他のものを注入することです。この場合、それは非ジェネリックのものであるべきです。その考えは、それをあなた自身のクラスでラップすることによって、あなたがユーザーがそれをどのように構成することができるかを完全に制御するということです。
デフォルトのアプローチはILogger<T>
です。これは、ログには特定のクラスのログが明確に表示されることを意味します。これらのログには完全なクラス名がコンテキストとして含まれるためです。例えば、あなたのクラスのフルネームがMyLibrary.MyClass
であるならば、あなたはこのクラスによって作成されたログエントリでこれを取得します。例えば:
MyLibrary.MyClass:情報:私の情報ログ
独自のコンテキストを指定したい場合はILoggerFactory
を使用してください。たとえば、あなたのライブラリからのすべてのログは、すべてのクラスではなく同じログコンテキストを持ちます。例えば:
loggerFactory.CreateLogger("MyLibrary");
そしてログは次のようになります。
MyLibrary:情報:私の情報ログ
すべてのクラスでこれを実行すると、コンテキストはすべてのクラスのMyLibraryになります。ログで内部クラス構造を公開したくない場合は、ライブラリに対してこれを実行することをお勧めします。
オプションのロギングについて。私はあなたが常にILoggerかILoggerFactoryをコンストラクターに要求し、それをオフにするためにそれをライブラリの消費者に任せるべきであると思います。設定内の特定のコンテキストに対してロギングを有効にすることは非常に簡単です。例えば:
{
"Logging": {
"LogLevel": {
"Default": "Warning",
"MyLibrary": "None"
}
}
}