はじめに
「デバッグ」と「トレース」は、アプリケーションの作成と保守に役立つ重要なプログラミング操作/プロセスです。デバッグとは、プログラミングエラーを観察して訂正することです。トレースはデバッグの一種であり、アプリケーションの「健康状態」を追跡することです(これは硬直的な定義であり、実際にはもっと広い意味も含まれます)。
アプリケーションの動作を動的に制御し、アプリケーションがどのように実行されているか、実行時にどんなエラーが発生するか、ピーク時にどんな動作をするか、アプリケーションの動作がどのような仕組みで動的に変化しているか、といった側面を追跡できるようにすることは非常に重要です。
この記事では、.NETでデバッグとトレースがどのように実装されているかと、これらの機能をニーズに合わせてカスタマイズする方法について説明します。
.NETでのトレース
まずは、.NETでトレースがどのように実装されているかを見てみましょう。.NETにはトレースリスナというオブジェクトが用意されています。トレースリスナとは、トレース出力を受け取って、それを他の場所に出力するオブジェクトです。開発環境内のウィンドウや、ハードディスク上のファイル、Windowsのイベントログ、SQL Serverデータベース、Oracleデータベースなど、さまざまなデータストアにトレース情報を出力することができます。
トレースリスナは、アプリケーションからのトレース情報を出力ストアに送り出すためのパイプのようなものです。トレース情報をトレースリスナに書き込むと、そのトレースリスナがトレース情報をターゲットメディアに出力してくれます。
トレース情報を何らかのデータストアに格納すると、後でそのデータを柔軟な方法で分析することができ、アプリケーション内の動作パターンとエラーやパフォーマンスとの関係を探るのに役立ちます。
System.Diagnostics
名前空間は、トレースやデバッグをはじめとする各種のアプリケーション診断サービスやシステム診断サービスで使用されるインターフェイス、クラス、列挙体、構造体を提供します。この記事では、トレースにのみ焦点を当てることにします。
System.Diagnostics
名前空間には、エラー情報とアプリケーション実行情報をログに書き込むために使用するTrace
とDebug
というクラスが用意されています。これらのクラスは、開発中にデバッグメッセージなどを出力するときや、開発後にパフォーマンス関連のパターンなどを出力するときに役立ちます。この2つのクラスは基本的には同じものですが、Trace
クラスのメソッドはリリースビルドコードの一部になるのに対して、Debug
クラスのメソッドはリリースビルド実行可能ファイルの一部にはなりません。
新しいプロジェクトを作成したときに、Debug
クラスによる出力を有効にするにはDEBUGシンボルを定義し、Trace
クラスによる出力を有効にするにはTRACEシンボルを定義します。Visual Studio .NETでプロジェクトを作成した場合は、そのプロジェクトのデバッグバージョンにはこれらのシンボルがあからじめ定義されています。
![図1 図1](http://cz-cdn.shoeisha.jp/static/images/article/138/020910_01.gif)
System.Diagnostics
名前空間には、いくつかの種類のトレースリスナが用意されています。既定のトレースリスナはSystem.Diagnostics.DefaultTraceListener
です。このクラスのWrite
メソッドとWriteLine
メソッドは、トレース情報をOutputDebugString
と関連デバッガのLog
メソッドに書き出します。
.NETフレームワークには、トレーススイッチという概念も導入されています。名前からわかるとおり、これはアプリケーションの外部から値を制御できるスイッチです。アプリケーションの外部から値を制御できるということは、アプリケーションをコンパイルして.exeファイルまたは.DLLファイルを生成した後に、コードを書き換えなくてもこれらのスイッチの値を変更できるということです。.configファイル内の対応するエントリを更新するだけで、アプリケーション内のスイッチに新しい値を割り当てることができます。より厳密に言えば、トレーススイッチとは、.configファイルを通じて外部から制御できる単純なオブジェクトということになります。
以降では、トレースリスナとトレーススイッチについて詳しく見ていきます。次に示すのは、Trace
クラスとDebug
クラスのWriteLine
メソッドの単純な使用例です。
Trace.WriteLine ("Hello 15Seconds Reader - Trace!") ; Debug.WriteLine ("Hello 15Seconds Reader - Debug!") ;
これらのメソッドをデバッグモードで使用すると、トレース出力とデバッグ出力がデバッガの出力ウィンドウに書き出されます(図2を参照)。
![図2 図2](http://cz-cdn.shoeisha.jp/static/images/article/138/020910_02.gif)
トレースリスナ
前述したとおり、トレースリスナはトレース情報を受け取り、格納し、最終的な宛先に送り出すためのオブジェクトです。トレース情報の最終的な宛先は、トレースリスナによって決定されます。.NETには次のようなトレースリスナが用意されています。
DefaultTraceListener
TextWriterTraceListener
EventLogTraceListener
すべてのトレースリスナはTraceListener
抽象クラスから派生しています。このクラスは、個々のトレースリスナが実装すべきメソッドを宣言しています。このクラスを継承する場合は、少なくともWrite
メソッドとWriteLine
メソッドを実装する必要があります。Trace
クラスとDebug
クラスには、リスナのコレクションへの参照を格納するListeners
というプロパティがあります。リスナのコレクションオブジェクトは、TraceListener
型のコレクションを表すTraceListenerCollection
型として扱われます。これはつまり、複数のリスナがトレース情報を使用できるということであり、これらのリスナはトレース情報の宛先について完全な制御権を持ちます。
Trace
クラスとDebug
クラスは同じTraceListenerCollection
オブジェクトを共有しています。したがって、Trace
オブジェクトにリスナを追加すると、そのリスナはDebug
でも使用可能になります(逆も同様です)。
すべてのトレースリスナは次のメソッドを備えています。これらのメソッドの機能は、トレース出力のターゲットメディアがトレースリスナによって決定されるという点を除いては、どのトレースリスナでも同じです。
メソッド名 | 結果 |
Fail | 指定されたテキストを呼び出し履歴とともに出力します。 |
Write | 指定されたテキストを出力します。 |
WriteLine | 指定されたテキストとキャリッジリターンを出力します。 |
Flush | ターゲットメディアへの出力バッファをフラッシュします。 |
Close | 出力ストリームを閉じて、トレース/デバッグ出力を受け取らないようにします。 |
DefaultTraceListener
このクラスは、Trace
クラスまたはDebug
クラスのTraceListenerCollection
オブジェクトに追加されている既定のトレースリスナです。このトレースリスナのFail
メソッドは、アプリケーションがユーザーインターフェイスモードで実行中であるときにはメッセージボックスを表示します。
このクラスは、Visual Studio環境内でトレース出力を確認したいときに使用します。Visual Studio .NETの外部でトレースメッセージを取得したい場合は、他のトレースリスナを使用する必要があります。
TextWriterTraceListener
TextWriterTraceListener
は、TextWriter
クラスのインスタンスまたはStream
クラスの任意のオブジェクト(ログファイル、ネットワークストリーム、コンソールなど)にトレース出力を書き出します。
次のコード例を見てください。
1. FileStream objStream = new FileStream("C:\\AppLog.txt", FileMode.OpenOrCreate) ; 2. TextWriterTraceListener objTraceListener = new TextWriterTraceListener(objStream) ; 3. Trace.Listeners.Add(objTraceListener) ; 4. Trace.WriteLine("Hello 15Seconds Reader -- This is first trace message") ; 5. Trace.WriteLine("Hello again -- This is second trace message") ; 6. Debug.WriteLine("Hello again -- This is first debug message") ; 7. Trace.Flush() ; 8. objStream.Close() ;
このコードは、TextWriterTraceListener
をログファイルに関連付ける方法、リスナのコレクションにリスナを追加する方法、トレース情報を書き出す方法を示しています。
1行目では、FileStream
型のオブジェクトを作成しています。このオブジェクトは、「C:\AppLog.txt」というアプリケーションのログファイルを指しています。
2行目では、TextWriterTraceListener
オブジェクトを作成し、先ほど作成したFileStream
オブジェクトをパラメータとして渡しています。このステートメントにより、「AppLog.txt」ファイルがトレース情報およびデバッグ情報の最終的な宛先として設定されます。
3行目では、トレースリスナをリスナのコレクションに追加しています。
4行目から6行目では、さまざまなトレースメッセージとデバッグメッセージを書き出しています。
7行目では、Trace
オブジェクトのFlush
メソッドを呼び出して、ターゲットメディアへの出力バッファをフラッシュしています。
8行目では、ログファイルを閉じています。
このコードを実行すると、ハードディスク上に「Applog.txt」ファイルが作成されます。「Applog.txt」ファイルの内容は図3のようになります。
![図3 図3](http://cz-cdn.shoeisha.jp/static/images/article/138/020910_03.gif)
このように、ログ情報をファイルに保存するのは非常に簡単です。
EventLogTraceListener
EventLogTraceListener
は、トレースメッセージとデバッグメッセージをWindowsのイベントログに書き出すときに使用します。このクラスの利点は、トレース情報とデバッグ情報をリモートコンピュータのイベントログにも書き出せることです。そのため、このクラスはイベントログをサポートしていないマシン(Microsoft Windows Meなど)でも役立ちます。
このクラスを使用してトレース情報とデバッグ情報をイベントログに書き出すには、まずこのクラスをイベントログに関連付ける必要があります。そのための手段として、EventLog
というプロパティが用意されています。このプロパティを使用すると、出力を受け取るイベントログを取得または設定できます。その他に、このクラスの3つ目のコンストラクタを使用するという方法もあります。このコンストラクタはイベントソースの名前を受け取り、クラスをイベントソースに自動的に関連付けます。
両方の方法を使用したコード例を次に示します。
例1:EventLog
オブジェクトを使用してEventLogTraceListener
を初期化する
EventLogTraceListener objListener = new EventLogTraceListener() ; EventLog objLog = new EventLog("EventLogName") ; objListener.EventLog = objLog ;
例2:コンストラクタを使用してEventLogTraceListener
を初期化する
EventLogTraceListener objListener = new EventLogTraceListener("EventLogName") ;
外部コードからトレースリスナを操作する
詳細なトレース情報を組み込んだアプリケーションをビルドし、実際の使用環境に配置したとします。このアプリケーションは順調に動作し、ハードディスク上にトレースログファイルを生成していました。そのうちに、アプリケーションの開発者の1人がトレース情報をデータベースに書き出すトレースリスナを新しく作成したので、それを使用したいと思います。さてどうすればいいでしょうか? コードを書き換えるという方法もありますが、あまりお勧めできません。コードを書き換えるのはトレースリスナを操作する1つの方法ではありますが、.NETには、これを実現する別の方法が用意されています。トレースリスナをコード内にハードコーディングする代わりに、アプリケーションの.configファイルを通じてトレースリスナを追加または削除できるのです。Windowsフォームアプリケーションの場合は、この設定ファイルの名前は「<アプリケーション名>.exe.config」となります。ASP.NETの場合は、「web.config」(マシンレベルの設定の場合は「machine.config」)となります。この設定ファイル内の<listeners>要素で、一部または全部のリスナを追加したり削除したりできます。次の例を見てください。この例では、既定のトレースリスナ(DefaultTraceListener
)を削除して、「MyEventLog」というイベントログにトレースメッセージを書き出すEventLogTraceListener
を追加しています。
<configuration> <system.diagnostics> <trace autoflush="true" indentsize="2"> <listeners> <remove name="Default"/> <add name="EventLogListener" type="System.Diagnostics.EventLogTraceListener,System" initializeData="MyEventLog"/> </listeners> </trace> </system.diagnostics> </configuration>
トレーススイッチ
ここまではアプリケーションにトレースコードやデバッグコードを組み込む方法を見てきましたが、トレース出力やデバッグ出力を制御する方法や、配置後にアプリケーションのトレース動作を変更する方法についてはまだ説明していません。
この種の制御を実現するには、トレーススイッチを使用します。トレーススイッチとは、Switch
クラスから派生したオブジェクトです。このオブジェクトはアプリケーションの設定ファイルを通じて制御できるので、トレース動作を変更しようとするたびに、コードを修正してコンパイルと配布をやり直す必要はなくなります。
スイッチオブジェクトには必ず名前と説明を割り当てます。スイッチオブジェクトの名前は、.configファイル内の対応するエントリを探す際に使用されるので重要な意味を持ちます。
トレーススイッチには、BooleanSwitch
とTraceSwitch
の2種類があります。以降では、この2種類のスイッチについて詳しく説明します。最初に注意しておきますが、トレーススイッチを使用するにはトレースとデバッグを有効にする必要があります。
BooleanSwitch
BooleanSwitch
は、その名前のとおり、有効(true
)または無効(false
)に設定できる二極スイッチです。
コード内でBooleanSwitch
を使用するには、BooleanSwitch
のインスタンスを作成する必要があります。このインスタンスには、.configファイル内で定義したスイッチと同じ名前を付けなければなりません。コード内のスイッチ名が.configファイル内のどのスイッチにも一致しない場合、そのスイッチは既定で無効になります。
次の.configファイルは、ShowErrorsというスイッチの例を示しています。これはエラー情報を出力するかどうかを表すためにコード内で使用されるスイッチであり、ここでは有効(value = "1"
)に設定されています。
<configuration> <system.diagnostics> <switches> <add name="ShowErrors" value="1" /> </switches> </system.diagnostics> </configuration>
次のコード例では、このスイッチの値に基づいて、エラー情報をコンソールに出力するかどうかを判断しています。
// Static class level declaration of BooleanSwitch object. static BooleanSwitch blnSwitch = new BooleanSwitch("ShowErrors", "Show errors to me") ; // If BooleanSwitch is enable, then report error/tracing info/etc. if (blnSwitch.Enabled == true) Console.WriteLine("Could not open connection to database") ;
<add>
タグのvalue
属性の値は、BoolenSwitch
の値を表します。したがって、次のようになります。
value
属性の値が0の場合は、Enabled
はfalse
value
属性の値が1の場合は、Enabled
はtrue
TraceSwitch
TraceSwitch
クラスでは、単純なBooleanSwitch
クラスよりも細かい制御ができます。このクラスでは、BooleanSwitch
クラスのような単純な有効/無効の制御ではなく、いくつかのレベルに分けた制御を行うことができます。TraceSwitch
には次のようなトレースレベルがあります。
トレースレベル | 設定値 | 説明 |
Off | 0 | トレースリスナにメッセージを出力しません。 |
Error | 1 | エラーメッセージだけをトレースリスナに出力します。 |
Warning | 2 | エラーメッセージと警告メッセージをトレースリスナに出力します。 |
Info | 3 | 情報メッセージ、警告メッセージ、エラーメッセージをトレースリスナに出力します。 |
Verbose | 4 | すべてのメッセージをトレースリスナに出力します。 |
TraceSwitch
クラスのインスタンスは、BooleanSwitch
オブジェクトと同様の方法で作成します。トレースを有効にするには、TraceSwitch
オブジェクトのLevel
プロパティを使用します。スイッチのLevel
プロパティを特定のレベル(上記の表のいずれかの値)に設定すると、指定のレベルよりも低いレベルをすべて含むことになります。たとえば、TraceSwitch
のLevel
プロパティをTraceLevel.Info
に設定した場合は、それよりも低いレベル、つまりTraceLevel.Error
からTraceLevel.Warning
までを含むことになります。
次のコードは、エラーメッセージと警告メッセージのみのトレースを有効にする方法を示しています。
TraceSwitch objSwitch = new TraceSwitch("ErrorAndWarningTrace", "This is the trace for Error and Warning messages") ; objSwitch.Level = TraceLevel.Warning ;
TraceSwitch
クラスには、その他に次のプロパティがあります。
TraceError
TraceWarning
TraceInfo
TraceVerbose
これらのプロパティはLevel
プロパティの値に連動しており、TraceSwitch
オブジェクトのLevel
プロパティを特定のレベル、たとえばTraceLevel.Warning
に設定した場合は、TraceError
プロパティとTraceWarning
プロパティはtrue
を返し、TraceInfo
プロパティとTraceVerbose
プロパティはfalse
を返します。これらのプロパティを使用すると、どの種類のメッセージがリスナに送信されるかをコード内で確認できます。これらのプロパティはTrace
クラスおよびDebug
クラスのWriteIf
メソッドとWriteLineIf
メソッドで使用されます。
次のコード例は、上記のトレースレベルプロパティを利用してトレース出力を制御する方法を示しています。
Trace.WriteLineIf(objSwitch.TraceWarning, "This is a warning message") ;
このステートメントでは、TraceSwitch
のトレースレベルが少なくともTraceLevel.Warning
に設定されているか、それより高い場合にのみ、警告メッセージを出力します。トレースレベルがTraceLevel.Error
の場合には、このステートメントは何も出力しません。