web-dev-qa-db-ja.com

ライブラリにILogger、ILogger <T>、ILoggerFactoryまたはILoggerProviderを使用する必要がありますか?

これは、 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を選択するため、いずれにしても失敗します。

想定ユーザーの頭痛を最小限に抑えながら、必要に応じて適切なロギングサポートを許可するためにここでやることに興味があります。

82
Michael Stum

定義

ILoggerILoggerProviderおよびILoggerFactoryの3つのインターフェースがあります。責任を見つけるために ソースコード を見てみましょう:

ILogger:主な役割は、指定されたログレベルのログメッセージを書き込むことです。

ILoggerProvider:責任は1つだけです。それはILoggerを作成することです。

ILoggerFactoryILoggerの作成と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のソースコードを見るとLoggerILoggersの配列(つまり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法則

解決されているクラスのすべてのコンストラクタは、独自の依存関係のセットを受け入れる以外には実装をしてはいけません。

51
Hooman Bahreini

ILoggerProvider以外はすべて有効です。 ILoggerとILogger <T>はあなたがロギングに使うべきものです。 ILoggerを取得するには、ILoggerFactoryを使用します。 ILogger <T>は特定のカテゴリのロガーを取得するためのショートカットです(カテゴリとしてのタイプへのショートカット)。

ILoggerを使用してロギングを実行すると、登録されている各ILoggerProvidersにそのログメッセージを処理する機会が与えられます。 ILoggerProviderに直接呼び出すコードを消費することは実際には無効です。

23
davidfowl

ILogger<T>はDIのために作られた実際のものです。 ILoggerは、asp.netコアで最も賢い決定の1つだったDIおよびFactoryロジックをすべて自分で作成する代わりに、ファクトリーパターンをはるかに簡単に実装できるようにするために生まれました。

次の中から選択できます。

あなたのコードでファクトリとDIパターンを使う必要があるならILogger<T>あるいはあなたはDIを必要とせずに簡単なロギングを実装するためにILoggerを使うことができます。

ILoggerProviderは、登録された各ログのメッセージを処理するための単なるブリッジです。それはあなたがコードに介在するべきである何にも影響を及ぼさないのでそれを使用する必要はありません。それは登録されたILoggerProviderを監視しメッセージを処理します。それはそれについてです。

7
Barr J

図書館デザインのための良いアプローチは次のようになります。

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)

5
Ihar Yakimush

質問にこだわり、私はILogger<T>が他のオプションの欠点を考慮して、正しいオプションであると思います:

  1. ILoggerFactoryを注入すると、ユーザーはクラスライブラリに可変グローバルロガーファクトリの制御権を放棄することになります。さらに、ILoggerFactoryを受け入れることで、クラスはCreateLoggerメソッドを使って任意のカテゴリ名でログに書き込むことができます。 ILoggerFactoryは通常DIコンテナ内のシングルトンとして利用可能ですが、私はユーザとして、どのライブラリがそれを使う必要があるのか​​疑問に思います。
  2. メソッドILoggerProvider.CreateLoggerはそのように見えますが、インジェクションを目的としていません。これはILoggerFactory.AddProviderと共に使用されるため、ファクトリは各登録プロバイダから作成された複数のILoggerに書き込む集約ILoggerを作成できます。これを調べると明らかです LoggerFactory.CreateLoggerの実装
  3. ILoggerを受け入れることもやり方のように見えますが、.NET Core DIでは不可能です。これは実際に最初にILogger<T>を提供する必要がある理由のように思えます。

つまり、これらのクラスから選択するのであれば、ILogger<T>よりも良い選択はありません。

別のアプローチは、非ジェネリックのILoggerをラップする何か他のものを注入することです。この場合、それは非ジェネリックのものであるべきです。その考えは、それをあなた自身のクラスでラップすることによって、あなたがユーザーがそれをどのように構成することができるかを完全に制御するということです。

5
tia

デフォルトのアプローチはILogger<T>です。これは、ログには特定のクラスのログが明確に表示されることを意味します。これらのログには完全なクラス名がコンテキストとして含まれるためです。例えば、あなたのクラスのフルネームがMyLibrary.MyClassであるならば、あなたはこのクラスによって作成されたログエントリでこれを取得します。例えば:

MyLibrary.MyClass:情報:私の情報ログ

独自のコンテキストを指定したい場合はILoggerFactoryを使用してください。たとえば、あなたのライブラリからのすべてのログは、すべてのクラスではなく同じログコンテキストを持ちます。例えば:

loggerFactory.CreateLogger("MyLibrary");

そしてログは次のようになります。

MyLibrary:情報:私の情報ログ

すべてのクラスでこれを実行すると、コンテキストはすべてのクラスのMyLibraryになります。ログで内部クラス構造を公開したくない場合は、ライブラリに対してこれを実行することをお勧めします。

オプションのロギングについて。私はあなたが常にILoggerかILoggerFactoryをコンストラクターに要求し、それをオフにするためにそれをライブラリの消費者に任せるべきであると思います。設定内の特定のコンテキストに対してロギングを有効にすることは非常に簡単です。例えば:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning",
      "MyLibrary": "None"
    }
  }
}
3
AlesD