.NET のログとトレース

コードをインストルメント化してログを生成することができ、プログラムの実行中に発生した興味深いイベントの記録として機能します。 アプリケーションの動作を理解するには、ログを確認します。 ログとトレースのどちらにも、この手法がカプセル化されています。 .NET には、その歴史を通じて複数の異なるログ API が蓄積されており、この記事は利用できるオプションを理解するのに役立ちます。

"ログ記録" と "トレース" という用語は、一般的に同意語です。 違いは、ログ出力は常に収集されることが予想されるため、オーバーヘッドが少ない必要があります。 トレースは通常、より侵襲性が高く、アプリケーションと .NET ランタイムのより深い部分からより多くの情報を収集します。 これは、特定の問題を診断するときに使用されるか、より詳細なパフォーマンス分析システムの一部として短時間、自動的に使用されます。

トレース用語のもう 1 つのピボットは、分散トレースです。 分散トレースは、要求ベースのシステムの大まかなアクティビティとタイミング データを収集し、サービス間で要求を関連付けて、各要求が完全なシステムによってどのように処理されるかを示します。

ログ API の主な違い

構造化ログ

ログ API は、構造化または非構造化にすることができます。

  • 非構造化: ログ エントリの内容は、人間が見ることを目的とした自由形式のテキストです。
  • 構造化: ログ エントリは、適切に定義されたスキーマを持ち、異なるバイナリ形式とテキスト形式にエンコードできます。 これらのログは、人と自動システムの両方が簡単に操作できるよう、機械翻訳とクエリが可能なように設計されています。

単純ではない用途には、構造化ログをサポートする API が適しています。 より高い機能、柔軟性、パフォーマンスを備えながら、使いやすさはほとんど違いません。

構成

単純なユース ケースには、コンソールまたはファイルに直接メッセージを書き込む API を使用できます。 ただし、ほとんどのソフトウェア プロジェクトでは、記録対象のログ イベントとその保持方法を構成すると便利です。 たとえば、ローカル開発環境で実行するときは、読みやすいようにプレーン テキストをコンソールに出力できます。 その後、アプリケーションを運用環境に展開するときに、専用データベースまたはローリング ファイルのセットにログを格納するように切り替えることができます。 オプションが適切に構成されている API では、これらの切り替えが簡単になりますが、構成可能なオプションが少ないと、変更するためにすべての場所のインストルメンテーション コードを更新する必要があります。

シンク

ほとんどのログ API では、ログ メッセージをシンクと呼ばれる異なる宛先に送信できます。 あらかじめ多数のシンクが作成されている API と、少数しかない API があります。 事前に作成されたシンクがない場合は、通常、カスタム シンクを作成できる拡張 API がありますが、その場合は記述する必要があるコードが少し増えます。

.NET のログ API

ILogger

既存のプロジェクトにログを追加するか、新しいプロジェクトを作成するかにかかわらず、ほとんどの場合、既定の選択肢には ILogger インフラストラクチャが適しています。 ILogger では、高速構造化ログ記録、柔軟な構成、コンソールを含む一般的なシンクのコレクションをサポートしています。これは、ASP.NET アプリの実行時に表示されます。 さらに、ILogger インターフェイスは、豊富な機能と拡張性を提供する多くのサードパーティのログ実装に対するファサードにもなります。

ILogger では、.NET 用の Open Telemetry 実装のログ記録のストーリーが提供されます。これにより、アプリケーションからさまざまな APM システムにログを送信して詳細な解析を行うことができます。

EventSource

EventSource は、構造化ログを使用する、古いハイ パフォーマンスのトレース API です。 もともとは Event Tracing for Windows (ETW) と適切に統合するように設計されていましたが、後でカスタム シンク用に EventPipe クロスプラットフォーム トレースと EventListener をサポートするように拡張されました。 ILogger と比べると、EventSource には事前に作成されたログ シンクが比較的少なく、独立した構成ファイルを使って構成するための組み込みのサポートがありません。 EventSource は、ETW または EventPipe との統合をより厳密に制御する必要がある場合は優れていますが、汎用のログの場合は、ILogger の方が柔軟性が高く、簡単に使用できます。

Trace

System.Diagnostics.TraceSystem.Diagnostics.Debug は、.NET の最も古いログ API です。 これらのクラスには柔軟な構成 API とシンクの大規模なエコシステムがありますが、非構造化ログしかサポートされていません。 .NET Framework では app.config ファイルを使って構成できますが、.NET Core には、ファイル ベースの構成メカニズムは組み込まれていません。 通常、デバッガーで実行中に開発者向けの診断出力を生成するために使用されます。 これらの API は下位互換性のため .NET チームによって引き続きサポートされますが、新しい機能は追加されません。 これらの API は、それらを既に使っているアプリケーションに適しています。 ログ API がまだ決まっていない新しいアプリでは、ILogger の方が機能的に優れている可能性があります。

特殊なログ API

コンソール

System.Console クラスの WriteWriteLine メソッドは、簡単なログ シナリオで使用できます。 これらの API はとても簡単に使い始めることができますが、そのソリューションは汎用ログ API ほど柔軟性がありません。 コンソールでは非構造化ログのみを使用でき、有効にするログ メッセージを選んだり、ターゲットを別のシンクに変更したりするための構成サポートはありません。 コンソール シンクで ILogger または Trace API を使っても、作業が大幅に増えることはなく、ログは構成可能なままです。

DiagnosticSource

System.Diagnostics.DiagnosticSource は、ログ メッセージをストレージにシリアル化するのではなく、インプロセスで同期的に分析することを目的とするログです。 このようにするとソースとリスナーは任意の .NET オブジェクトをメッセージとして交換できるのに対し、ほとんどのログ API ではログ イベントがシリアル化可能である必要があります。 また、この手法は非常に高速で、リスナーが効率的に実装されていれば、数十ナノ秒でログ イベントを処理できます。 これらの API を使うツールの動作は、多くの場合、むしろインプロセス プロファイラーに近いものですが、それに対して API ではどのような制約も課されません。

EventLog

System.Diagnostics.EventLog は Windows 専用の API であり、Windows EventLog にメッセージを書き込みます。 多くの場合、ILogger と、Windows で実行するときにオプションで EventLog シンクを使うと、アプリと Windows OS を緊密に結合することなく、同様の機能を提供できます。