かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

EnterpriseLibrary 6のSemantic Logging Application Blockを触ってみた

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);
        }


    }
}