アプリケーションのイベント出力


WEB キャスト

このデモの内容

このデモでは、Windows Eventing 6.0 の ETW (Event Tracing for Windows) を使用した、イベントログの出力方法について簡単に説明します。
このデモをご覧いただく前に、以下の知識を理解しておいてください。

■トレースソース (TraceSource) とトレースリスナー (TraceListener)

トレースを出力するアプリケーション側について説明します。トレースのオン/オフや、スイッチの切り替えなどを柔軟に実装できるように、.NET Framework には、トレースの出力をおこなうトレースソース (TraceSource) と、出力されたトレースを処理するトレースリスナー (TraceListener) と呼ばれるクラスが存在しています。ユーザアプリケーションのコードを作成する開発者は、実際にトレース出力がおこなわれるか否かに関係なく、アプリケーションの必要な箇所で、TraceSourceを使用して必要なすべてのトレースの出力処理を記述しておきます。そして、必要におうじて出力方法を記述したカスタムの TraceListener を作成しておき、構成ファイル (.config) の設定のみで、どのトレースソースに対してどのトレースリスナーを使用して出力をおこなうか、どのレベルの出力をおこなうか、などを設定することができます。

■Windows Eventing 6.0

出力されたイベントをそのままリスナーからデータベースやテキストなどに出力しても良いのですが、Windows では、より統一的で拡張可能な仕組みを提供しています。例えば、IIS 7 (Inernet Information Services 7.0) が標準で提供しているトレースの仕組みを使用すると、IIS 以外の ASP.NET などの他のトレース結果なども一緒に出力をおこない、これらのトレースログを時系列に並べて分析することなどが可能になっています。こうした統一的なトレースの仕組みには、ETW (Event tracing for Windows) と呼ばれる Operating System が提供する仕組みが利用されています。
ユーザアプリケーション側は、扱うイベントの種類を「イベントプロバイダ」と呼ばれる論理的なオブジェクトとして作成してETWに登録し、上述のリスナーなどを使用してこの登録されたプロバイダに対して書き込み (WriteEvent) をおこなうのみです。あとはアプリケーションと分離された「イベントトレースセッション」と呼ばれるセッションを ETW が処理します。結果の出力や加工には、ETW に付属のさまざまなアプリケーション (このアプリケーションもカスタムで構築をおこなうことができます) を使用して、ユーザアプリケーションとは別の世界でこれらトレースの情報をリアルタイムに処理することができるようになっています。

イベントプロバイダ (Provider)

イベントトレースセッションにイベントを書き込む論理的なオブジェクトです。カスタムなイベントプロバイダを作成することで、独自のスキーマを定義して、独自の収集項目 (例えば、閲覧された商品の ID 番号など) を出力し、あとで統計処理をおこなうことなどができます。
Windows には、HTTP.SYS (IIS)、ASP.NET Events などのさまざまなプロバイダが既に登録されており、いつでもこれらのトレースセッションを開始できるようになっています。(コマンドプロンプトで logman query providers と入力するとプロバイダの一覧を参照できます)

コントローラ (Controller application)

プロバイダの有効化/無効化、トレースセッションの開始/停止などの制御をおこないます。Windows には、tracelog.exe、logman.exe などのコントローラアプリケーションが既に存在します。また、イベントビューア (イベントログ) からも GUI を使用してこうした制御をおこなうことができるようになっており、内部でこのコントローラの仕組みが実装されています。

コンシューマ (Consumer application)

イベントのデータを読み取って処理するプログラムのことです。コールバックの仕組みによって、リアルタイムに読み込みをおこなうこともできます。コマンドラインの tracerpt.exe もコンシューマの1つですし、Windows Server 2008 のイベントビューアや Visual Studio 自身にもこうしたコンシューマの仕組みが搭載されています。WCF のトレースデータをビジュアルに表示するサービス トレース ビューア ツール (Service Trace Viewer Tool , SvcTraceViewer.exe) もこうしたコンシューマアプリケーションの 1 つです。

デモでご紹介しているソースコード

(EventProvider クラスは .NET Framework 3.5 から導入された新しいクラスですので、Visual Studio 2008 が必要です)

【イベントマニフェスト (XML)】

<?xml version="1.0" encoding="utf-16"?>
<instrumentationManifest 
      xmlns="https://schemas.microsoft.com/win/2004/08/events" 
      xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events"
      xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <instrumentation>
    <events>
      <!--Publisher Info -->
      <provider name="Microsoft-Windows-EventLogSampleMywork" 
        guid="{616A7E02-3B2F-4657-958E-AFAFF9ACED9C}" 
        symbol="MICROSOFT_SAMPLE_MYWORK" 
        resourceFileName="C:\Demo\CustomTraceDemo\CustomTraceListener\bin\Debug\CustomTraceListener.dll" 
        messageFileName="C:\Demo\CustomTraceDemo\CustomTraceListener\bin\Debug\CustomTraceListener.dll"
        parameterFileName="C:\Demo\CustomTraceDemo\CustomTraceListener\bin\Debug\CustomTraceListener.dll">
        <!--Channel to which this Publisher can publish -->
        <channels>
          <!--Pre-Existing channel can be imported, but not required. -->
          <importChannel chid="C1" name="Application"/> 
          <!--New Channel can be declared for this Publisher-->
          <channel chid="MyChannel" 
            name="Microsoft-Windows-EventLogSampleMywork/Operational" 
            type="Operational" 
            symbol="SAMPLE_MYWORK" 
            isolation="Application" enabled="true"/>
        </channels>
        <!--Event Templates -->
        <templates>
          <template tid="MyEventTemplate">
            <data name="TestMessage" inType="win:UnicodeString" />
            <data name="TestDescription" inType="win:UnicodeString" />
            <UserData>
              <MyEvent2 xmlns="myNs">
                <TestMessage>%1</TestMessage>
                <TestDescription>%2</TestDescription>
              </MyEvent2>
            </UserData>
          </template>
        </templates>
        <!--All the Events that can be published by this Publisher -->
        <events>
          <event value="1" 
            template="MyEventTemplate" 
            channel="MyChannel" 
            symbol="PROCESS_INFO_EVENT"
            message="$(string.Publisher.EventMessage)"/>
        </events>
      </provider>
    </events>
  </instrumentation>
  <localization>
    <resources culture="en-US">
      <stringTable>
        <!--This is how event data can be used as part of Message String -->
        <string id="Publisher.EventMessage" 
          value="TestMessage=%1;%n
          TestDescription=%2"/>
      </stringTable>
    </resources>
  </localization>
</instrumentationManifest>

【トレースリスナー クラス (C#)】

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Diagnostics.Eventing;
namespace Microsoft.Sample
{
    public class MyTraceListener1 : TraceListener
    {
        public MyTraceListener1() { }
        protected void TraceEventInternal (TraceEventCache eventCache, 
string data1, string data2, int eventId, TraceEventType eventType)
        {
            EventDescriptor evtDesc = new EventDescriptor(eventId, 0, 0, 
(byte) ToEventDescriptorLevel(eventType), 0, 0, 0);
            EventProvider evtProvider = new EventProvider(new Guid("{616A7E02-3B2F-4657-958E-AFAFF9ACED9C}"));
            evtProvider.WriteEvent(ref evtDesc, data1, data2);
        }
        public override void TraceEvent(TraceEventCache eventCache, 
string source, TraceEventType eventType, int id, string message)
        {
            TraceEventInternal(eventCache, message, null, id, eventType);
        }
        public override void TraceEvent(TraceEventCache eventCache, 
string source, TraceEventType eventType, int id, string format, params object[] args)
        {
            TraceEventInternal(eventCache, (string)args[0], (string) args[1], id, eventType);
        }
        public override void TraceData(TraceEventCache eventCache, 
string source, TraceEventType eventType, int id, object data)
        {
            throw new NotImplementedException();
        }
        public override void TraceData(TraceEventCache eventCache, 
string source, TraceEventType eventType, int id, params object[] data)
        {
            throw new NotImplementedException();
        }
        public override void Write(string message)
        {
            WriteLine(message);
        }
        public override void WriteLine(string message)
        {
            TraceEventInternal(null, message, null, 0, TraceEventType.Verbose);
        }
        // TraceEventType から EventDescriptor の Level に変換するためのヘルパ関数
        enum EventDescriptorLevel
        {
            Critical = 1,
            Error,
            Warning,
            Information,
            Verbose
        }
        EventDescriptorLevel ToEventDescriptorLevel(TraceEventType type)
        {
            EventDescriptorLevel result;
            switch (type)
            {
                case TraceEventType.Critical :
                    result = EventDescriptorLevel.Critical;
                    break;
                case TraceEventType.Error:
                    result = EventDescriptorLevel.Error;
                    break;
                case TraceEventType.Warning:
                    result = EventDescriptorLevel.Warning;
                    break;
                case TraceEventType.Information:
                    result = EventDescriptorLevel.Information;
                    break;
                default :
                    result = EventDescriptorLevel.Verbose;
                    break;
            }
            return result;
        }
    }
}

【トレースソースを使用したトレースの出力 (C#)】

TraceSource source = new TraceSource("MyTraceSource");
source.TraceEvent(TraceEventType.Information, 1, 
"msg={0}, dsc={1}", new object[] { "test1", "This is information." });
source.TraceEvent(TraceEventType.Error, 1, "msg={0}, dsc={1}", new object[] { "test2", "This is error." });

【構成ファイルの設定 (.config)】

<?xml version="1.0"?>
<configuration>
  . . . . . . .

  <system.diagnostics>
    <sources>
      <source name="MyTraceSource" switchName="sourceSwitch" switchType="System.Diagnostics.SourceSwitch">
        <listeners>
          <add name="MyETW" type="Microsoft.Sample.MyTraceListener1, CustomTraceListener"/>
        </listeners>
      </source>
    </sources>
    <switches>
      <add name="sourceSwitch" value="Warning"/>
    </switches>
  </system.diagnostics>
  . . . . . . .

</configuration>

ページのトップへ