かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

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


    }
}