WCF入門-10。System.Diagnostics.TraceListenerを継承してカスタムトレースリスナを作成する。

サンプルコードWCFService-10.zip 直

WCFは通信のリクエスト毎にスレッドを立ち上げてクライアントの要求に答えるのでトレースリスナはスレッドセーフである必要があります。System.Diagnostics.XmlWriterTraceListenerはスレッドセーフではないので、カスタムトレースリスナを作成してスレッドセーフなトレースリスナを作成してみます。

カスタムトレースリスナの最小構成はSystem.Diagnostics.TraceListenerを継承して、WriteメソッドとWriteLineメソッドをオーバーライドしたものになります、下のコードにトレース情報を保存する処理を実装します。今回はファイルに保存する処理をスレッドセーフにしただけの物を作成しますが、トレース情報をDBに保存したり、別のノードにデータを送信したり、メールで送信したり、自由に実装を変更できます。


Imports System.Diagnostics

Public Class XMLTraceListener
Inherits System.Diagnostics.TraceListener

Public Overloads Overrides Sub Write(ByVal message As String)

End Sub

Public Overloads Overrides Sub WriteLine(ByVal message As String)

End Sub
End Class

スレッドセーフであるというのは、あるリソースを使用する場合に別スレッドが同時に同じ処理をした場合でも問題ない、または別スレッドの同時実行を防ぐ仕組み(排他制御)を実装している。という実装をスレッドセーフといいます。カスタムトレースリスナで言えば、ファイルに同時に書き込んでもファイルが壊れないログの内容が混ざらないなどの特性を備えているかどうかという事になります。

スレッドセーフにするためにはSyncLockを使用します。SyncLockを使用することでSyncLockで囲まれたステートメントが同一プロセス内で別スレッドが同時に実行されることが無いことが保証されます。クリティカルセクションとも言います。保護するリソースをSyncLockで囲みます。今回はファイルへの出力部分をSyncLockで保護します。


Imports System.Diagnostics

Public Class XMLTraceListener
Inherits System.Diagnostics.TraceListener

Private _traceLock As Object = New Object
Private _fileName As String = "default.log"

Public Overloads Overrides Sub Write(ByVal message As String)
InnerWrite(message)
End Sub

Public Overloads Overrides Sub WriteLine(ByVal message As String)
InnerWrite(message)
End Sub

Private Sub InnerWrite(ByVal message As String)

SyncLock _traceLock
System.IO.File.AppendAllText(_fileName, message)
End SyncLock

End Sub

End Class

作成したカスタムトレースリスナをアプリケーション構成ファイル(app.config)に設定します。記載方法は「厳密クラス名, アセンブリ名」です。厳密クラス名は、名前空間を省略しない書き方です。WCFInterface名前空間にXMLTraceListenerクラスがある場合、厳密クラス名はWCFInterface.XMLTraceListenerとなります。アセンブリ名は、ルート名前空間と同じ場合が多いですが、別の名前を設定することもできます。図の例では、アセンブリ名にWCFInterfaceAssemblyName、ルート名前空間にWCFInterfaceを設定しています。

"System.Diagnostics.XmlWriterTraceListener"の設定例





"WCFInterface.XMLTraceListener"の設定例





自作したカスタムとレースリスナを使用する場合アセンブリ名まで記載しなければいけないので注意する必要があります。「厳密クラス名, アセンブリ名」という書き方は、アプリケーション構成ファイルでクラスを指定する場合の一般的な書き方です。アセンブリ名を書き忘れたり、#namespace "hogehoge"としているのを忘れて厳密クラス名が不完全だと、以下のようなエラーが発生します。


System.Configuration.ConfigurationErrorsException はハンドルされませんでした。
BareMessage="クラス WCFInterface.XMLTraceListener の型が見つかりませんでした。"
場所 System.Diagnostics.TraceUtils.GetRuntimeObject(String className, Type baseType, String initializeData)

また、カスタムトレースリスナが同一アセンブリ内で定義されている場合は問題ありませんが、別のアセンブリで実装をしているのに、それを参照設定していないと同様のエラーが発生します。

また、アプリケーション構成ファイルで出力するログファイル名を指定している場合(initializeData= "c:\service.svclog")は、ファイル名を受け付けるコンストラクタを実装していないと以下のようなエラーが発生します。


System.Configuration.ConfigurationErrorsException はハンドルされませんでした。
BareMessage="WCFInterface.XMLTraceListener, WCFInterfaceAssemblyName を作成できませんでした。"
Message="WCFInterface.XMLTraceListener, WCFInterfaceAssemblyName を作成できませんでした。"
場所 System.Diagnostics.TraceUtils.GetRuntimeObject(String className, Type baseType, String initializeData)

この場合は、ファイル名を受け付けるコンストラクタを実装します。






Imports System.Diagnostics

Public Class XMLTraceListener
Inherits System.Diagnostics.TraceListener

Private _traceLock As Object = New Object
Private _fileName As String = "default.log"

Public Sub New(ByVal fileName As String)
_fileName = fileName
End Sub

Public Overloads Overrides Sub Write(ByVal message As String)
InnerWrite(message)
End Sub

Public Overloads Overrides Sub WriteLine(ByVal message As String)
InnerWrite(message)
End Sub

Private Sub InnerWrite(ByVal message As String)

SyncLock _traceLock
System.IO.File.AppendAllText(_fileName, message)
End SyncLock

End Sub

End Class