.NET アプリケーションでのログとトレース
アプリケーションの開発を続けて複雑さが増した場合、追加のデバッグ診断をアプリケーションに適用することが必要になります。
トレースは、実行中にアプリケーションの実行を監視するための手段です。 トレースとデバッグのインストルメンテーションは、開発時に .NET アプリケーションに追加できます。 アプリケーションの開発中と配置後に、そのインストルメンテーションを使用できます。
この単純な手法は、驚くほど強力です。 デバッガーを超えるものが必要な状況でこれを使用できます。
- 長期間にわたって発生する問題は、従来のデバッガーではデバッグが難しい場合があります。 ログを使用すると、長期間にわたる詳細な事後レビューを行うことができます。 これに対し、デバッガーはリアルタイム分析に制約されます。
- 多くの場合、マルチスレッド アプリケーションと分散アプリケーションはデバッグが困難です。 デバッガーをアタッチすると、動作が変更される傾向があります。 複雑なシステムを理解するために、必要に応じて詳細なログを分析できます。
- 分散アプリケーションの問題は、多くのコンポーネント間の複雑な相互作用から生じることがあります。 デバッガーをシステムのすべての部分に接続することは合理的ではない場合があります。
- 多くのサービスは停止することができません。 デバッガーをアタッチすると、多くの場合、タイムアウト エラーが発生します。
- 問題は常に予測できるとは限りません。 ログとトレースは、問題が発生した場合に備えてプログラムで常に記録できるように、オーバーヘッドが低くなるように設計されています。
出力ウィンドウに情報を書き込む
ここまで、コンソールを使用して、アプリケーション ユーザーに情報を表示してきました。 モバイル、Web、デスクトップ アプリなどのユーザー インターフェイスを持ち、表示されるコンソールがない、.NET で構築されるその他の種類のアプリケーションがあります。 これらのアプリケーションでは、System.Console
が "表示されない" メッセージを記録します。これらのメッセージは、Visual Studio や Visual Studio Code の出力ウィンドウに表示されることがあります。 また、Android の logcat
のようなシステム ログに出力される場合もあります。 そのため、コンソール以外のアプリケーションで System.Console.WriteLine
を使用する場合は、十分な検討を行う必要があります。
この場合、System.Console
に加えて System.Diagnostics.Debug
と System.Diagnostics.Trace
を使用できます。 Debug
と Trace
はどちらも System.Diagnostics
の一部であり、適切なリスナーがアタッチされている場合にのみ、ログへの書き込みが行われます。
どの出力スタイル API を使用するかは、ユーザーが決定します。 主な違いを次に示します。
- System.Console
- 常に有効であり、常にコンソールに書き込みます。
- 顧客がリリースで確認する必要のある情報に役立ちます。
- 最も簡単な方法であるため、アドホックな一時デバッグによく使用されます。 このデバッグ コードは、多くの場合、ソース管理にチェックインされません。
- System.Diagnostics.Trace
TRACE
が定義されている場合にのみ有効になります。- アタッチされているリスナーに書き込みます。既定では DefaultTraceListener です。
- この API は、ほとんどのビルドで有効にするログを作成するときに使用します。
- System.Diagnostics.Debug
DEBUG
が定義されている場合にのみ有効になります (デバッグ モードの場合)。- アタッチされたデバッガーに書き込みます。
- この API は、デバッグ ビルドでのみ有効にするログを作成するときに使用します。
Console.WriteLine("This message is readable by the end user.");
Trace.WriteLine("This is a trace message when tracing the app.");
Debug.WriteLine("This is a debug message just for developers.");
トレースとデバッグの戦略を立てるときは、出力をどのように表示するかを検討します。 関連のない情報が設定された複数の Write ステートメントは、読みにくいログを作成します。 その一方で、WriteLine を使用して関連のあるステートメントを別々の行に出力すると、どの情報が関連し合っているかを読み取るのが困難になります。 一般に、複数のソースの情報を組み合わせて 1 つの情報メッセージを作成する場合は、複数の Write ステートメントを使用します。 1 つの完全なメッセージを作成する場合は、WriteLine ステートメントを使用します。
Debug.Write("Debug - ");
Debug.WriteLine("This is a full line.");
Debug.WriteLine("This is another full line.");
この出力は、Debug
を使用した前のログのものです。
Debug - This is a full line.
This is another full line.
TRACE と DEBUG の各定数を定義する
既定では、アプリケーションがデバッグ状態で実行されるときは、DEBUG
定数が定義されます。 これは、プロパティ グループのプロジェクト ファイルに DefineConstants
エントリを追加することによって制御できます。 Debug
構成の DEBUG
に加えて、Debug
と Release
の両方の構成に対して TRACE
をオンにする例を次に示します。
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
デバッガーにアタッチされていないときに Trace
を使用する場合は、dotnet-trace などのトレース リスナーを構成する必要があります。
条件付きトレース
単純な Write
および WriteLine
メソッドに加えて、WriteIf
と WriteLineIf
を使用して条件を追加する機能もあります。 例として、次のロジックは、カウントがゼロかどうかを確認し、デバッグ メッセージを書き込みます。
if(count == 0)
{
Debug.WriteLine("The count is 0 and this may cause an exception.");
}
これは、1 行のコードに書き直すことができます。
Debug.WriteLineIf(count == 0, "The count is 0 and this may cause an exception.");
また、これらの条件は、Trace
や、アプリケーションで定義したフラグでも使用できます。
bool errorFlag = false;
System.Diagnostics.Trace.WriteIf(errorFlag, "Error in AppendData procedure.");
System.Diagnostics.Debug.WriteIf(errorFlag, "Transaction abandoned.");
System.Diagnostics.Trace.Write("Invalid value for data request");
特定の条件が存在することを確認する
アサーション (Assert
ステートメント) は、Assert
ステートメントの引数として指定した条件をテストします。 条件の評価が true
の場合、アクションは発生しません。 条件の評価が false
の場合、アサーションは失敗します。 デバッグ ビルドで実行している場合、プログラムは中断モードになります。
Debug
または Trace
から Assert
メソッドを使用できます。これらは、System.Diagnostics
名前空間にあります。 Debug
クラスのメソッドはプログラムのリリース バージョンには含まれないので、リリース コードのサイズを増加させたり処理速度を低下させることはありません。
System.Diagnostics.Debug.Assert
メソッドを自由に使用して、コードが正しい場合に true になる条件をテストできます。 たとえば、整数の除算関数を記述したとします。 数学の規則により、0 での除算は不可能です。 アサーションを使用して、この条件をテストできます。
int IntegerDivide(int dividend, int divisor)
{
Debug.Assert(divisor != 0, $"{nameof(divisor)} is 0 and will cause an exception.");
return dividend / divisor;
}
このコードをデバッガーで実行すると、アサート ステートメントが評価されます。 ただし、リリース バージョンではこの比較が行われないため、追加のオーバーヘッドはありません。
Note
System.Diagnostics.Debug.Assert
を使用する場合は、Assert が削除される場合、Assert
内のコードによってプログラムの結果が変化しないことを確認します。 そうしないと、プログラムのリリース バージョンにのみ見られるバグが誤って発生する可能性があります。 関数またはプロシージャの呼び出しが含まれるアサートに特に注意してください。
System.Diagnostics
名前空間の Debug
と Trace
を使用すると、アプリケーションの実行およびデバッグ時に追加のコンテキストが的確にわかります。