Windows のイベントログをファイルに出力する方法を比較してみた

イベントログをファイルに出力する必要性に駆られたので調べてみました。

# 各方法の比較
注意事項:

- 出力時間は「Win7 x86 Pro で 60000 個くらい溜まってる System ログ」を出した時のものです
- 各コマンド自体は Windows Server 2012 Std x86_64 でも動くことを確認しています（ので最近の Windows なら問題無く使えると思います）
- powershell のコマンドラインは表記の制約上、パイプを全角で書いてます

| 方法 | 出力時間 | 出力形式 | コマンドライン |
| ---- | -------- | -------- | -------------- |
| winapi BackupEventLog() | 0.5秒 | EVTX | `backupeventlog.exe` |
| wevtutil epl            | 1秒   | EVTX | `wevtutil epl System system.evtx` |
| wevtutil qe XML         | 6秒   | XML  | `wevtutil qe System /f:XML /rd:true  > system.txt` |
| wevtutil qe Text        | 45秒  | Text | `wevtutil qe System /f:Text /rd:true > system.txt` | 
| powershell Get-EventLog | 160秒 | Text | `powershell -Command "Get-EventLog system ｜ Format-List" > system.txt` |

# どの方法を採用すべきか？
以下の条件次第で、どの方法を使うかが自ずと決まってきます。

- 出力時間は早い方が良い？遅くても良い？
- 出力形式は .evtx？それともテキスト？
- powershell は使ってもよい？wevtutil は使ってもよい？C言語で自製しても良い？

個人的には wevtutil が無難だと思いました。出力時間も短いし、evtx-text 間変換もできるし、とバランスが良いです。

# 各方法についての解説
大まかにはC言語で自製、wevtutilコマンドがニパターン、PowerShell、と三通りの方法があります。

## 方法1: Windows API の BackupEventLog() 関数
Windows API の [BackupEventLog 関数](https://msdn.microsoft.com/ja-jp/library/cc428922.aspx) を使う方法です。C言語でコンパイルすること前提ですが、おそらく最も早く出力できる方法だと思います。

しかし .evtx 形式であるため、イベントビューアでないと見れません。

ソース:

```c
#include <stdlib.h>
#include <windows.h>
int main(int argc, char *argv[])
{
    HANDLE h = OpenEventLog(NULL, "system");
    BackupEventLog(h, "system.evtx");
    CloseEventLog(h);
    return 0;
}
```

## 方法2: wevtutil epl
イベントログを扱うのに wevtutil という便利なコマンドがあります。こいつの epl オプションを使うと .evtx 形式で出力できます。

方法1に迫る勢いで処理が早いので、下手に自製するくらいならこの方法で良いと思います。しかし .evtx 形式なのでやはりイベントビューアが必要です。

## 方法3: wevtutil qe
wevtutil の別オプション qe を使えば、テキストで出力することもできます。

フォーマットは二種類あって、/f:Text で人間が読めるテキスト形式、/f:XML で XML 形式(人間が読むのは辛い)です。

処理時間は XML の方がだいぶ早いです。どうも内部的には XML がデフォとなっているらしく、/f:Text の場合はデフォの XML からわざわざ変換処理が走っているため遅いのだと考えられます。

## 方法4: PowerShell の Get-EventLog コマンドレット
PowerShell にも [Get-Eventlog](https://technet.microsoft.com/ja-jp/library/ee176846.aspx) というコマンドレットがあります。

スクリプトなので PowerShell に精通しているならあれこれ出来て便利だと思います。が、他の方法と比べると処理時間が遅いのが難点でしょうか。（測ってませんが）CPU 消費率も高い気がしますし。

# 出力内容サンプル
ここではテキストで出力する方法について、どんな感じに出力されるかのサンプルを挙げてみました。

※オプションの `-newest 1` や `/c:1` は「最新一件のみ取得」を意味します。これをしないと何万というログがずらずら出て確認しづらいため。

## powershell Get-Eventlog
情報が全然足りませんね。

```
$ powershell -Command "Get-EventLog system -newest 1"

   Index Time          EntryType   Source                 InstanceID Message
   ----- ----          ---------   ------                 ---------- -------
  177539 9 15  9:27    Information Service Control M...   1073748860 Application ...
```

## powershell Get-EventLog からの Format-List
Format-List により足りない情報も見えています。

```
$ powershell -Command "Get-EventLog system -newest 1 | Format-List"
Index              : 177539
EntryType          : Information
InstanceId         : 1073748860
Message            : Application Experience サービスは 実行中 状態に移行しました。
Category           : (0)
CategoryNumber     : 0
ReplacementStrings : {Application Experience, 実行中}
Source             : Service Control Manager
TimeGenerated      : 2017/09/15 9:27:10
TimeWritten        : 2017/09/15 9:27:10
UserName           :
```

## wevtutil qe テキスト形式

```
$ wevtutil qe System /c:1 /f:Text /rd:true
Event[0]:
  Log Name: System
  Source: Service Control Manager
  Date: 2017-09-15T09:27:10.676
  Event ID: 7036
  Task: N/A
  Level: 情報
  Opcode: N/A
  Keyword: クラシック
  User: N/A
  User Name: N/A
  Computer: MYCOMPUTERNAME
  Description:
Application Experience サービスは 実行中 状態に移行しました。
```

## wevtutil qe XML形式
人間が読むものではないですね。

```
$ wevtutil qe System /c:1 /f:XML /rd:true
<Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'><System><Provider Name='Service Control Manager' Guid='{555908d1-a6d7-4695-8e1e-26931d2012f4}' EventSourceName='Service Control Manager'/><EventID Qualifiers='16384'>7036</EventID>...(中略)...</Event>
```

# おわりに
イベントログを出力する方法を比べてみました。他にも良い方法があったら教えてください。