Logging Application Blockというログのがあるにも関わらず、Semantic Logging Application Blockというのが追加されてます。こいつはタイプセーフにログのAPIが作れて、いい感じだぜ?みたいなノリっぽいけど、ちゃんとドキュメント読んでないのでよくわかりません。
因みにETW(Event Tracing for Windows)(イベントログじゃないYO)を裏で使うみたいです。ETWについては、以下のBlogが詳しい感じです。因みにわたしはETWが何なのかよく知りません。
ログAPIの作り方
これは、Semantic Logging Application Blockの機能というよりは.NET 4.5から追加されたEventSourceクラスの使い方になります。
こいつをちゃんと使うと、タイプセーフなロギングAPIができるって寸法ですね。例えばこんな感じ。
[EventSource(Name = "MyEventSource")] class MyEventSource : EventSource { /// <summary> /// キーワード /// </summary> public static class Keywords { public const EventKeywords Diagnostic = (EventKeywords)1; public const EventKeywords Lifecycle = (EventKeywords)2; } /// <summary> /// タスク /// </summary> public static class Tasks { public const EventTask DB = (EventTask)1; public const EventTask App = (EventTask)2; } private MyEventSource() { } // シングルトンのインスタンス private static MyEventSource instance = new MyEventSource(); public static MyEventSource Log { get { return instance; } } // 各種ログAPI [Event(1, Keywords = Keywords.Lifecycle, Level = EventLevel.Informational, Message = "Process start", Opcode = EventOpcode.Start, Task = Tasks.App)] public void Start() { this.WriteEvent(1); } [Event(2, Keywords = Keywords.Lifecycle, Level = EventLevel.Informational, Message = "Process stop", Opcode = EventOpcode.Stop, Task = Tasks.App)] public void Stop() { this.WriteEvent(2); } [Event(3, Keywords = Keywords.Diagnostic, Level = EventLevel.Verbose, Message = "Execute qyeru {0}", Opcode = EventOpcode.Info, Task = Tasks.DB)] public void Query(string query) { this.WriteEvent(3, query); } }
こいつで、以下のように使うと、ETWにログが出るっていう寸法です。
MyEventSource.Log.Start();
MyEventSource.Log.Query("select * from dual");
MyEventSource.Log.Stop();
Semantic Logging Application Blockを使う
ETWのに出たログを監視して、いろんなところに出してくれるのがSemantic Logging Application Blockみたいです。NuGetでEnterpriseLibrary Semanticあたりで検索してSemantic Logging Application Blockを追加します。
そして、アプリケーションの最初の初期化処理あたりで以下のようなコードを書きます。
// リスナー作って var l = new ObservableEventListener(); // 監視するログの種類を設定して l.EnableEvents( MyEventSource.Log, EventLevel.Verbose, MyEventSource.Keywords.Diagnostic | MyEventSource.Keywords.Lifecycle); // 何処にログを出すか決める l.LogToConsole();
こうすると、先ほどのコードで出力したログがETWを経由してコンソールに出るようになります。実行すると以下のような感じのログが出ます。
EventId : 1, Level : Informational, Message : Process start, Payload : , EventName : AppStart, Timestamp : 2014-07-12T14:56:50.0105184Z, ProcessId : 9736, ThreadId : 8384 EventId : 3, Level : Verbose, Message : Execute qyeru select * from dual, Payload : [query : select * from dual] , EventName : DBInfo, Timestamp : 2014-07-12T14:56:50.0265193Z, ProcessId : 9736, ThreadId : 8384 EventId : 2, Level : Informational, Message : Process stop, Payload : , EventName : AppStop, Timestamp : 2014-07-12T14:56:50.0265193Z, ProcessId : 9736, ThreadId : 8384
LogToConsoleには、どのような形式で出力するか設定するフォーマッターも設定できて、JSON形式なんかで出すこともできます。
l.LogToConsole(new JsonEventTextFormatter());
出力が以下のようになります。
{"ProviderId":"8983a2e6-c5d2-5a1f-691f-db243cb1f681","EventId":1,"Keywords":2,"L evel":4,"Message":"Process start","Opcode":1,"Task":2,"Version":0,"Payload":{}," EventName":"AppStart","Timestamp":"2014-07-12T14:58:30.2691727Z","ProcessId":124 08,"ThreadId":7540},{"ProviderId":"8983a2e6-c5d2-5a1f-691f-db243cb1f681","EventI d":3,"Keywords":1,"Level":5,"Message":"Execute qyeru select * from dual","Opcode ":0,"Task":1,"Version":0,"Payload":{"query":"select * from dual"},"EventName":"D BInfo","Timestamp":"2014-07-12T14:58:30.3161730Z","ProcessId":12408,"ThreadId":7 540},{"ProviderId":"8983a2e6-c5d2-5a1f-691f-db243cb1f681","EventId":2,"Keywords" :2,"Level":4,"Message":"Process stop","Opcode":2,"Task":2,"Version":0,"Payload": {},"EventName":"AppStop","Timestamp":"2014-07-12T14:58:30.3431778Z","ProcessId": 12408,"ThreadId":7540},
まとめ
とりあえずコンソールに出るようになりました。LogTo****メソッドでほかにファイルやAzureのテーブルやSQL Databaseなんかに出すこともできるみたいです。
コード全体
一応コード全体のせておきます。
using Microsoft.Practices.EnterpriseLibrary.SemanticLogging; using Microsoft.Practices.EnterpriseLibrary.SemanticLogging.Formatters; using System.Diagnostics.Tracing; namespace ConsoleApplication7 { class Program { static void Main(string[] args) { // リスナー作って var l = new ObservableEventListener(); // 監視するログの種類を設定して l.EnableEvents( MyEventSource.Log, EventLevel.Verbose, MyEventSource.Keywords.Diagnostic | MyEventSource.Keywords.Lifecycle); // 何処にログを出すか決める l.LogToConsole(new JsonEventTextFormatter()); MyEventSource.Log.Start(); MyEventSource.Log.Query("select * from dual"); MyEventSource.Log.Stop(); } } [EventSource(Name = "MyEventSource")] class MyEventSource : EventSource { /// <summary> /// キーワード /// </summary> public static class Keywords { public const EventKeywords Diagnostic = (EventKeywords)1; public const EventKeywords Lifecycle = (EventKeywords)2; } /// <summary> /// タスク /// </summary> public static class Tasks { public const EventTask DB = (EventTask)1; public const EventTask App = (EventTask)2; } private MyEventSource() { } // シングルトンのインスタンス private static MyEventSource instance = new MyEventSource(); public static MyEventSource Log { get { return instance; } } // 各種ログAPI [Event(1, Keywords = Keywords.Lifecycle, Level = EventLevel.Informational, Message = "Process start", Opcode = EventOpcode.Start, Task = Tasks.App)] public void Start() { this.WriteEvent(1); } [Event(2, Keywords = Keywords.Lifecycle, Level = EventLevel.Informational, Message = "Process stop", Opcode = EventOpcode.Stop, Task = Tasks.App)] public void Stop() { this.WriteEvent(2); } [Event(3, Keywords = Keywords.Diagnostic, Level = EventLevel.Verbose, Message = "Execute qyeru {0}", Opcode = EventOpcode.Info, Task = Tasks.DB)] public void Query(string query) { this.WriteEvent(3, query); } } }