Trace から TraceSource へ
.NET 1.1 における標準的で低レベルなデバッグ情報は、System.Diagnostics.Debug クラスと System.Diagnostics.Trace クラス行える。*1 .NET 2.0 では、さらに System.Diagnostics.TraceSource クラスが追加され、こちらを利用することが推奨になっている。
MSDN: TraceSource
TraceSource は拡張されたトレース システムとして機能するためのもので、以前の Trace トレース クラスと Debug トレース クラスの静的メソッドの代わりに使用できます。前バージョンの Trace クラスと Debug クラスも引き続き使用できますが、トレースには TraceSource クラスを使用することをお勧めします。
TraceSource はインスタンスが必要
TraceSource クラスの多くのメソッドは Debug クラスや Trace クラスと同様に ConditionalAttribute によって TRACE とマークされておりコンパイルオプション等によってその機能の多くを停止できるようになっている。また、TRACE とマーキングされていることから、Debug クラスよりも Trace クラスに近い用途で利用されることが想像できる。
Trace クラスと TraceSource クラスの大きな違いは、TraceSource クラスはインスタンス生成を必要とするというところだろう。
(1) Trace.WriteLine("msg..."); (2) TraceSource source = new TraceSource("Name"); source.TraceEvent(TraceEventType.Verbose, 1, "msg...");
(1) の記述方法だと、TRACE が未定義の環境であればコンパイルされた IL イメージからは綺麗サッパリ消滅するのだが、(2) の記述方法では、1行目の TraceSource の生成処理は TRACE が見て意義であっても IL イメージ上に残る。これはちょっとしたことを嫌うような人にとって TraceSource を敬遠する理由になってしまうのではないかと思われる。
TraceSource と SourceSwitch
さて、肝心の TraceSource の機能だが、Trace クラスが WriteLine(), WriteLineIf(), Indent() といった非常に低レベルな操作と BooleanSwitch や TraceSwitch のような低レベルなスイッチ設定*2しかサポートしていないのに対して、TraceSource クラスでは、TraceData() および TraceEvent() と、比較的抽象的な操作を提供し、対応したスイッチ SourceSwitch も TraceSource に対応した設計になっています。
TraceSwitch と SourceSwitch はトレースレベルという同じようなものを設定するようになっていますが、TraceSwitch は設定値を読んだり設定値を見てトレース処理を変化させるのはコンシューマ側で制御する項目であるのに対し、SourceSwitch はトレースの出力の可否を判断する基準としてフレームワーク化されています。
Trace.Refresh()
.NET 2.0 になって、スイッチの設定などのアプリケーション構成ファイルの System.Diagnostics セクションを書き換えた場合には、このメソッドを呼び出すことで再読み込みを実施してくれるようになっている。繰り返し可能な操作に対するトレースをアプリケーションを再起動することなく On/Off できるため結構便利なので覚えておくとよいかもしれない。
TraceListner を構成ファイルから作成・設定する
id:ladybug:20041006#p6 の終盤の Main() の実装では、TraceListener のインスタンスを生成して設定するコードが実装されているが、この部分に関しては本来はコンパイルされたアセンブリには含める必要がない処理である。この処理を1つのメソッドにまとめて ConditionalAttribute でマークすればいいのだが、.NET 2.0 からは TraceListner をアプリケーション構成ファイルから追加することができるようになっている。場所は
<System.Diagnostics> <Trace autoflush="false" indentsize="4"> <Listeners> <add type="type" initializeData="data" name="name" /> </Listeners> </Trace> </System.Diagnostics>
type 属性でクラス名を指定することは必須であるが、initializeData と name は省略可能になっている。name 属性はリスナに名前をつけて判別するために必要になればつけるもので、initializeData はリスナに与えるパラメータとなっている。標準で提供されるリスナは、
クラス名 | initializeData | 機能 |
---|---|---|
ConsoleTraceListener | bool | Console.Out(false,省略時) または Console.Error(true) へ出力する |
TextWriterTraceListener | ファイル名 | ファイルへ出力する |
XmlWriterTraceListener | ファイル名 | XML ファイルへ出力する |
DelimitedListTraceListener | ファイル名 | ";"区切り*3でファイルへ出力する |
EventLogTraceListener | ソース名 | 指定したソースへイベントログとして出力する |
となっている。最初の2つはベタなログを出力し、残りの3つは構造化された出力を行うようになっている。
TraceSource へ TraceListner 設定する
TraceSource も、Trace 同様にコードおよび構成ファイルから TraceListner を追加することができる。記述セクションが
フラグ | 説明 | 例 |
---|---|---|
DateTime | 日付時刻を含める | DateTime=2006-02-07T10:42:56.0436250Z |
Timestamp | タイムスタンプを含める | Timestamp=105372520914645 |
Callstack | 呼び出し履歴を含める | Callstack=場所 Program.Main()*4 |
ProcessId | プロセス ID を含める | ProcessId=3136 |
ThreadId | スレッド ID を含める | ProcessId=346 |
LogicalOperationStack | 論理演算履歴を含める | LogicalOperationStack=ヘッダ読み込み, ファイル読み込み |
複数指定するときはカンマで区切って指定する。
<configuration> <system.diagnostics> <sources> <source name="TraceTest" switchName="sw" switchType="System.Diagnostics.SourceSwitch"> <listeners> <add type="System.Diagnostics.ConsoleTraceListener" initializeData="true" traceOutputOptions="DateTime, Callstack" /> </listeners> </source> </sources> <switches> <add name="sw" value="Information"/> </switches> </system.diagnostics> </configuration>
public static Image LoadFromFile(string filename) { Trace.CorrelationManager.StartLogicalOperation("ファイル読み込み"); : } public void SaveToFile(string filename) { Trace.CorrelationManager.StartLogicalOperation("ファイル書き込み"); : } void ReadHeader() { Trace.CorrelationManager.StartLogicalOperation("ヘッダ読み込み"); : } void Seek(Stream stream, long pos) { traceSource.TraceInformation("seek to " + pos); }
のようなかんじになっていて、読み書き両方の処理から Seek() が呼び出されている場合、CallStack オプションを使用して呼び出し履歴を舐めるよりも、LogicalOperationStack をみればファイルの読み書きどちらから Seek() が呼び出されているかひと目で判断することができる。
sharedListners
Trace にも TraceSource にも、複数の TraceSource にも、共通の TraceListener を設定するためには、
<configuration> <system.diagnostics> <sources> <source name="TraceTest" switchName="sw" switchType="System.Diagnostics.SourceSwitch"> <listeners> <add name="console" /> </listeners> </source> <sources> <source name="TraceTest2" switchName="sw2" switchType="System.Diagnostics.SourceSwitch"> <listeners> <add name="console" /> </listeners> </source> </sources> <switches> <add name="sw" value="Information"/> <add name="sw2" value="Error"/> </switches> <sharedListeners> <add name="console" type="System.Diagnostics.ConsoleTraceListener" initializeData="true" traceOutputOptions="DateTime, Callstack" /> </sharedListeners> </system.diagnostics> </configuration>
*1:参項 id:ladybug:20041006#p2
*2:参項 id:ladybug:20041006#p4
*3:Delimiter プロパティで変更可能
*4:これは Main() から直接呼び出しているためで、例外などの Stacktrace プロパティのように複数行で記録されます