次の方法で共有


.NET ライブラリ作成者向けのログガイダンス

ライブラリの作成者として、ログ記録を公開することは、ライブラリの内部動作に関する分析情報をコンシューマーに提供する優れた方法です。 このガイダンスは、他の .NET ライブラリやフレームワークと一貫性のある方法でログ記録を公開するのに役立ちます。 また、他の方法では明らかではない可能性がある一般的なパフォーマンスのボトルネックを回避するのにも役立ちます。

ILoggerFactory インターフェイスを使用するタイミング

ログを出力するライブラリを作成するときは、ログを記録するために ILogger オブジェクトが必要です。 そのオブジェクトを取得するには、API で ILogger<TCategoryName> パラメーターを受け入れるか、ILoggerFactoryを呼び出した後にILoggerFactory.CreateLoggerを受け入れることができます。 どの方法を推奨する必要がありますか?

  • ログを出力できるように、複数のクラスに渡すことができるログ オブジェクトが必要な場合は、 ILoggerFactoryを使用します。 各クラスは、クラスと同じ名前の個別のカテゴリでログを作成することをお勧めします。 これを行うには、ログを出力するクラスごとに一意の ILogger<TCategoryName> オブジェクトを作成するファクトリが必要です。 一般的な例としては、ライブラリのパブリック エントリ ポイント API や、ヘルパー クラスを内部的に作成する可能性がある型のパブリック コンストラクターなどがあります。

  • 1 つのクラス内でのみ使用され、共有されないログ オブジェクトが必要な場合は、 ILogger<TCategoryName>を使用します。ここで、 TCategoryName はログを生成する型です。 この一般的な例は、依存関係の挿入によって作成されたクラスのコンストラクターです。

時間の経過と同時に安定している必要があるパブリック API を設計する場合は、将来的に内部実装をリファクタリングする必要がある可能性があることに注意してください。 クラスが最初に内部ヘルパー型を作成しない場合でも、コードの進化に伴って変更される可能性があります。 ILoggerFactoryを使用すると、パブリック API を変更することなく、新しいクラスの新しいILogger<TCategoryName> オブジェクトを作成できます。

詳細については、「 フィルター規則の適用方法」を参照してください。

ソース生成ログを優先する

ILogger API では、API を使用するための 2 つの方法がサポートされています。 LoggerExtensions.LogErrorLoggerExtensions.LogInformationなどのメソッドを呼び出すか、ログ ソース ジェネレーターを使用して厳密に型指定されたログメソッドを定義できます。 ほとんどの状況では、優れたパフォーマンスとより強力な入力を提供するため、ソース ジェネレーターをお勧めします。 また、メッセージ テンプレート、ID、ログ レベルなどのログ固有の懸念事項を呼び出し元のコードから分離します。 ソース生成以外の方法は、主に、コードをより簡潔にするためにそれらの利点を放棄するシナリオに役立ちます。

using Microsoft.Extensions.Logging;

namespace Logging.LibraryAuthors;

internal static partial class LogMessages
{
    [LoggerMessage(
        Message = "Sold {Quantity} of {Description}",
        Level = LogLevel.Information)]
    internal static partial void LogProductSaleDetails(
        this ILogger logger,
        int quantity,
        string description);
}

前述のコード:

  • partial class という名前の LogMessages を定義します。これは、static 型の拡張メソッドを定義できるようにするため ILogger です。
  • LogProductSaleDetails属性とLoggerMessage テンプレートを使用して、Message拡張メソッドを装飾します。
  • LogProductSaleDetailsを拡張し、ILoggerquantityを受け入れるdescriptionを宣言します。

ヒント

デバッグ中にソース生成コードをステップ インできます。これは、それを呼び出すコードと同じアセンブリの一部であるためです。

IsEnabledを使用して、高価なパラメーター評価を回避する

パラメーターの評価にコストがかかる場合があります。 前の例を拡張して、 description パラメーターがコンピューティングコストの高い string であるとします。 おそらく、販売される製品はわかりやすい製品の説明を取得し、データベース クエリやファイルからの読み取りに依存します。 このような状況では、ソース ジェネレーターに対して、 IsEnabled ガードをスキップし、呼び出しサイトで IsEnabled ガードを手動で追加するように指示できます。 これにより、ユーザーはガードが呼び出される場所を判断し、計算にコストがかかる可能性があるパラメーターが本当に必要なときにのみ評価されるようにすることができます。 次のコードについて考えてみましょう。

using Microsoft.Extensions.Logging;

namespace Logging.LibraryAuthors;

internal static partial class LogMessages
{
    [LoggerMessage(
        Message = "Sold {Quantity} of {Description}",
        Level = LogLevel.Information,
        SkipEnabledCheck = true)]
    internal static partial void LogProductSaleDetails(
        this ILogger logger,
        int quantity,
        string description);
}

LogProductSaleDetails拡張メソッドが呼び出されると、IsEnabled ガードが手動で呼び出され、コストの高いパラメーター評価は必要な場合に制限されます。 次のコードについて考えてみましょう。

if (_logger.IsEnabled(LogLevel.Information))
{
    // Expensive parameter evaluation
    var description = product.GetFriendlyProductDescription();

    _logger.LogProductSaleDetails(
        quantity,
        description);
}

詳細については、「 コンパイル時のログ ソースの生成 」と 「.NET での高パフォーマンスログ」を参照してください。

ログ記録で文字列補間を回避する

一般的な間違いは、 文字列補間 を使用してログ メッセージを作成することです。 対応する LogLevel が有効になっていない場合でも文字列が評価されるため、ログ記録での文字列補間はパフォーマンスに問題があります。 文字列補間の代わりに、ログ メッセージ テンプレート、書式設定、および引数リストを使用します。 詳細については、「 .NET でのログ記録: ログ メッセージ テンプレート」を参照してください。

ログ用にno-opのデフォルト設定を使用する

ILoggerまたはILoggerFactoryが必要なログ API を公開するライブラリを使用するときに、ロガーを提供したくない場合があります。 このような場合、 Microsoft.Extensions.Logging.Abstractions NuGet パッケージでは、no-op ログの既定値が提供されます。

が指定されていない場合、ライブラリ コンシューマーは既定で ILoggerFactory記録できます。 null ログの使用は、型が null 以外であるため、null 許容 (ILoggerFactory?) として型を定義するのとは異なります。 これらの利便性を目的とした型は何もログに記録せず、基本的に何の操作も行いません。 可能な場合は、使用可能な抽象化の種類のいずれかを使用することを検討してください。