Windowsのイベントログを日付期間で抽出する

# はじめに
会社では運用チームに所属しています。
先日、管理しているWindows Server 2008R2のサーバーのWebアプリケーションにてOracleのモジュールで障害が発生し、IISの保護機能によりアプリケーションプールの１つが自動停止しました。
IISリセットすることで早急に復旧することが出来たのですが、再度発生しないように対策はしなければなりません。

某社のサポートに入っているので障害報告と原因究明を依頼しました。
某社からご提供して欲しい情報として、Oracleのlistner.logやalert.logの他にイベントログのアプリケーションとシステムをテキストで欲しいとのこと。

# イベントログの出力
実はイベントログの日付期間で抽出しテキスト出力する方法がすぐに分からず、ネットで探し回ったので分かったことを書いておきます。

イベントビューアーと`wevtutil`コマンドを使用した2パターンを提示します。
※出力フォーマットに違いがあります。イベントビューアーによる出力は1イベント1行ですが、`wevtutil`コマンドは1イベント複数行となります。

## イベントビューアーの使用
下記をWindowsログ配下にある「Application」と「システム」の2つに対して行います。

 1. イベントビューアーを起動します。
 2. 操作パネルにある「現在のログをフィルター...」をクリックします。
 3. 現在のログをフィルター画面が起動します。
 4. ログの日付 項目にて「ユーザー設定の範囲」に変更します。
 5. ユーザー設定の範囲画面にて開始日と終了日を「次の日時」に変更後に対象期間を入力して `OK` ボタンをクリックします。
 6. イベントレベル 項目にて必要なチェックボックスにチェックを付けます。
 7. `OK` ボタンをクリックします。
 8. フィルターされた一覧が抽出されます。
 9. 一覧を全選択します。(一番上行を選択しておき、スクロールバーで最後に移動して最終行にて `Shift`キーを押しながら選択)
 10. 操作パネルにある「選択したイベントの保存...」をクリックします。
 11. 名前を付けて保存ダイアログ画面にて、ファイルの種類を「テキスト(タブ区切り)(*.txt)」もしくは「CSV(コンマ区切り)(*.csv)」にして、ファイル名を入力して保存ボタンをクリックします。
 12. 操作パネルにある「フィルターのクリア」をクリックします。

## wevtutilコマンドの使用
コマンドプロンプト画面にて`wevtutil`コマンドを使用することで、イベントログを日付期間で抽出して出力することが出来ます。

今回使用している`wevtutil`のオプションは以下のとおり。

* [Windows コマンド  サーバーの役割別のコマンド  wevtutil](https://docs.microsoft.com/ja-jp/windows-server/administration/windows-commands/wevtutil)
* [wevtutil でイベントログをフィルタリングするサンプル](http://stakiran.hatenablog.com/entry/2018/04/25/193335)

```bash:オプション
wevtutil qe application /f:text "/q:xxxx"
qe ・・・ イベントログからデータを取得する際に指定するオプション
f  ・・・ 出力形式
q  ・・・ 抽出条件
```

アプリケーションログとシステムログを 11/18 10:30～12:00 でテキスト出力

```bash:例
rem アプリケーションログ
wevtutil qe application /f:text "/q:*[System[TimeCreated[@SystemTime>='2019-11-18T01:30:00' and @SystemTime<='2019-11-18T03:00:00']]]" > アプリケーションログ.txt

rem システムログ
wevtutil qe system /f:text  "/q:*[System[TimeCreated[@SystemTime>='2019-11-18T01:30:00' and @SystemTime<='2019-11-18T03:00:00']]]" > システムログ.txt
```

**【注意点】**
日時指定がグリニッジ標準時(GMT)となっているので日本の標準時(JST)にするには、9時間前を指定する必要があります。
例 11/18 10:30～12:00 で抽出
開始日時 2019-11-18T10:30:00 → 2019-11-18T01:30:00
終了日時 2019-11-18T12:00:00 → 2019-11-18T03:00:00

イベントビューアーの「現在のログをフィルター...」のフィルターにて、ログの日付 項目で開始日と終了日を指定した後にXMLタブを見ると9時間前に自動変換されています。

### 9時間前を計算
バッチで日時計算をするのは面倒くさいんですよね、そんな時は日付時刻計算にVBScriptを使ってしまうのが楽です。

VBSのDateAddした際に日時がゼロサプレスになってしまい、0埋めさせる簡単な方法を模索したら、.NETの"System.Text.StringBuilder"を使用する方法を見つけました。
[VBScriptでのFormat関数の代わり (書式文字列を使いたい)](http://blog.livedoor.jp/tea_cocoa_cake/archives/7578885.html)

【2020/03/22追記】
>Windows 8、Windows 8.1、Windows Server 2012以降では、.NET 3.5 が最初からインストールされていません。
.NET 4.5 からは、.NET型をCOMインターフェイスとして公開することを停止したため、"System.Text.StringBuilder" や "System.Collections.ArrayList" などがエラーになるようになりました。
このため、Windows 10 ユーザーに配布するには注意が必要です。
>
>対応としては、.NET 3.5をインストールすることで使えるようになります。
>[【VBS】.NETを使用した書式指定(Format)の文字列を取得する](https://qiita.com/yaju/items/63212e39a67d1d0122cf)

```bash:9時間前対応
@echo off
cd /d %~dp0

set StartDate=2019-11-23T10:30:00
set EndDate=2019-11-23T12:00:00

rem 9時間前に変換
call :CnvDate %StartDate%
set StartDate=%RETVAL%
call :CnvDate %EndDate%
set EndDate=%RETVAL%

rem イベントログ出力
wevtutil qe application /f:text "/q:*[System[TimeCreated[@SystemTime>='%StartDate%' and @SystemTime<='%EndDate%']]]" > application.txt

wevtutil qe system /f:text "/q:*[System[TimeCreated[@SystemTime>='%StartDate%' and @SystemTime<='%EndDate%']]]" > system.txt

exit

rem GMTからJSTに変換
:CnvDate
echo dim sb1:set sb1=CreateObject("System.Text.StringBuilder"):sb1.AppendFormat_4 "{0:yyyy-MM-ddTHH:mm:ss}",Array(DateAdd("h",-9,Replace("%1","T"," "))):WScript.Echo sb1.toString > tmp.vbs
for /f "tokens=1" %%A in ('cscript //nologo tmp.vbs') do set RETVAL=%%A
del tmp.vbs

exit /b 
```

# 最後に
.NETの"System.Text.StringBuilder"で書式変換できるのを見つけたのは、今回の成果でしたね。