2018年8月14日火曜日

C# ETW(Event Tracing for Windows)へログを出力する


ETW (Event Tracing for Windows) は Windows OS のイベントトレースの仕組みです。ETW を使用するとログの出力とログの記録を分離することができ、アプリケーションのパフォーマンス向上などのメリットがあります。C# アプリケーションから ETW へログを出力してみます。



ログ出力メソッドを自作する


ログ出力メソッドを実装するクラスは EventSource クラスを継承させて実装し、EventSourceAttribute を付与します。
この属性には Name プロパティと Guid プロパティを設定することができ、これらは ETW から読み取るときに必要になるプロバイダ名/IDとして使用されます。

// using System.Diagnostics.Tracing;

[EventSource(
Name = "mxProject.TestEtwLogger"
, Guid = "{FAE1403F-2B75-493D-96C4-12B330928A97}"
)]
public class EtwLogger : EventSource {
}


EtwStream を使用してログの読み取りを行ってみたところ、Name プロパティと Guid プロパティの両方を指定した場合、Name プロパティの値では読み取ることができませんでした。

NameGuid読み取るときに指定するプロバイダ名/ID
Name を指定する。
Guid を指定する。
Guid を指定する。Name では読み取りできない。


ログ出力メソッドには EventAttribute を付与します。

  • 第1引数の数値はイベントIDです。WriteEvent メソッドの第1引数と合わせます。
  • Message プロパティは、ETW の「フォーマット済メッセージ」を生成するための書式文字列です。プレイスホルダはログ出力メソッドの引数と置き換えられます。
  • Level プロパティは、ETW の「イベントレベル」として設定する値です。ログのフィルタリングのための値としては、Keywords, OpCode, Task などのプロパティも指定することができます。
  • ログ出力メソッドの引数はETWの「ペイロード」として出力されます。引数名がペイロード名として使用されます。下の例では time, message, memberName, filePath, line の5つになります。

// using System.Diagnostics.Tracing;
// using System.Runtime.CompilerServices;

[Event(1, Level = EventLevel.Informational, Message = "{2}:{1}")]
public void Info(
  DateTime time
  , string message
  , [CallerMemberName] string memberName = ""
  , [CallerFilePath] string filePath = ""
  , [CallerLineNumber] int line = 0
)
{
  WriteEvent(1, time, message, memberName, filePath, line);
}


EtwStream で紹介されているロガーや後述する NLog.Etw で実装されているロガーもこの手順で実装されています。


NLog を使う


NLog から ETW にログを出力するための拡張ライブラリはいくつか公開されています。その中の一つである NLog.Etw を使用する方法を説明します。

設定ファイル(NLog.config)を使用して構成する場合、ターゲットに ExtendedEventTracing を指定します。

<?xml version="1.0" encoding="utf-8" ?>
<nlog
  xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>

  <!-- アセンブリに NLog.Etw を指定する -->
  <extensions>
    <add assembly="NLog.Etw" />
  </extensions>

  <!-- ターゲットに ExtendedEventTracing を指定する -->
  <targets async="true">

    <target xsi:type="ExtendedEventTracing" name="eetw">
      <layout xsi:type="JsonLayout">
        <attribute name="level" layout="${level}" />
        <attribute name="counter" layout="${counter}" />
        <attribute name="timestamp" layout="${longdate}" />
        <attribute name="message" layout="${message}" />
      </layout>
    </target>

  </targets>

  <!-- ルールにターゲットを指定する -->
  <rules>
    <logger name="*" minlevel="Debug" writeTo="eetw" />
  </rules>
  
</nlog>


ETW プロバイダー名は "LowLevelDesign-NLogEtwSource" になります。
ペイロードに出力される内容は次の通りです。

ペイロード名内容
LoggerNameILogger インスタンスに設定したロガー名
Message指定されたレイアウトでフォーマットされたメッセージ

「フォーマット済メッセージ」には、上記の LoggerName と Message を連結した文字列("{Loggername}: {Message}")になります。

なお、JsonLayout で JSON エンコードを行いたくない場合は、encode 属性で false を指定します。※ 4.1 以降のみ
<attribute name="message" layout="${message}" encode="false" />

0 件のコメント:

コメントを投稿

paiza のスキルチェックをやってみました

いまさら感はありますが、 paiza のスキルチェックをやってみました。指定された時間内にコードを書いてユニットテストにかけ、その結果を基に評価を数値化してくれるというものですが、ゲーム感覚で空き時間を見つけて進めていこうと考えています。 どうやら時間が短いほど高い評価を得...