Windowsはシステムやアプリケーションの動作状況をイベントログに記録しています。「サーバーが落ちていた」「ディスクエラーが出ていた」といったトラブルを後から気づくのではなく、発生と同時に検知して通知を受け取る仕組みをバッチファイルで構築できます。
本記事では wevtutil コマンドを使ったイベントログの検索方法から、複数EventIDの監視・時間範囲フィルタリング・通知処理・タスクスケジューラとの連携まで、実務で使えるレベルを目指して解説します。
この記事で学べること
- イベントログの基本構造(チャンネル・レベル・EventID)
wevtutilのXPathクエリでの検索方法- 複数EventIDや特定ソース・レベルを条件にした検出
- 時間範囲を絞って直近のエラーだけを取得する方法
- 検出時の通知方法(ファイル記録・ポップアップ・PowerShell連携)
- タスクスケジューラで定期監視を自動化する方法
イベントログの基本構造を理解する
バッチでイベントログを扱う前に、構造を把握しておくと後のクエリ設計がスムーズになります。
| 概念 | 説明 | 主な値の例 |
|---|---|---|
| チャンネル(ログ名) | ログの種類・保存場所 | System、Application、Security |
| レベル | イベントの深刻度 | 1=Critical、2=Error、3=Warning、4=Information |
| EventID | イベントの種類を識別する番号 | 1001(アプリクラッシュ)、7036(サービス停止) |
| Provider(ソース) | イベントを記録したコンポーネント | disk、Service Control Manager |
| TimeCreated | イベントが発生した日時 | XPathクエリで範囲指定可能 |
wevtutil でイベントログを検索する基本
wevtutil qe(query events)コマンドを使うと、条件を絞ってイベントログを取得できます。
wevtutil qe <ログ名> /q:"<XPathクエリ>" /f:text /c:<件数>
| オプション | 意味 |
|---|---|
qe |
イベントクエリ(query events) |
/q:"..." |
XPath形式の検索条件 |
/f:text |
テキスト形式で出力(他に xml、RenderedXml) |
/c:N |
取得件数の上限(最新N件) |
/rd:true |
最新から順に取得(デフォルトは古い順) |
wevtutil qe System /q:"*[System[(EventID=7036)]]" /f:text /c:1 /rd:true
注意:管理者権限が必要なケースがあります
Securityログは管理者権限がないと読み取れません。System・Application ログは通常ユーザーでも読み取れますが、タスクスケジューラで実行する場合は「最上位の特権で実行」にチェックを入れておくと安全です。
特定のEventIDを検出してログに記録する(基本スクリプト)
まず最もシンプルなパターンとして、特定のEventIDが存在するかを確認し、見つかればログファイルに記録するスクリプトです。
@echo off
setlocal
set "EVENT_ID=7036"
set "LOG_NAME=System"
set "TMPFILE=%~dp0tmp_event.txt"
set "LOGFILE=%~dp0event_alert.log"
:: 最新1件を一時ファイルに出力
wevtutil qe %LOG_NAME% /q:"*[System[(EventID=%EVENT_ID%)]]" /f:text /c:1 /rd:true > "%TMPFILE%" 2>&1
:: 取得できたか(ファイルが空でないか)確認
for %%F in ("%TMPFILE%") do (
if %%~zF == 0 (
echo [%DATE% %TIME%] EventID %EVENT_ID% は検出されませんでした。 >> "%LOGFILE%"
del "%TMPFILE%"
exit /b 0
)
)
:: 検出された場合
echo [%DATE% %TIME%] EventID %EVENT_ID% を検出しました。 >> "%LOGFILE%"
type "%TMPFILE%" >> "%LOGFILE%"
echo ---------------------------------------- >> "%LOGFILE%"
del "%TMPFILE%"
endlocal
ポイント:ファイルサイズで空チェックする
wevtutil は条件に一致するイベントがなくても終了コード0を返します。findstr での文字列チェックより、%%~zF(ファイルサイズ取得)で空判定する方が確実です。
複数のEventIDをまとめて監視する
XPathクエリは or 条件を使って複数のEventIDを同時に監視できます。
@echo off
setlocal
set "LOG_NAME=System"
set "TMPFILE=%~dp0tmp_event.txt"
set "LOGFILE=%~dp0event_alert.log"
:: EventID 7036(サービス停止)または 41(予期しないシャットダウン)を検出
set "QUERY=*[System[(EventID=7036 or EventID=41)]]"
wevtutil qe %LOG_NAME% /q:"%QUERY%" /f:text /c:5 /rd:true > "%TMPFILE%" 2>&1
for %%F in ("%TMPFILE%") do (
if %%~zF == 0 (
echo [%DATE% %TIME%] 対象イベントなし >> "%LOGFILE%"
del "%TMPFILE%"
exit /b 0
)
)
echo [%DATE% %TIME%] 異常イベントを検出しました。 >> "%LOGFILE%"
type "%TMPFILE%" >> "%LOGFILE%"
echo ======================================== >> "%LOGFILE%"
del "%TMPFILE%"
endlocal
レベル(Error/Warning)を条件に絞り込む
EventIDを指定せず、「エラーレベル以上のイベントをすべて監視する」といった使い方もできます。
@echo off
setlocal
:: Level=1:Critical, Level=2:Error, Level=3:Warning
:: Level<=2 でCriticalとErrorの両方を対象にする
set "QUERY=*[System[(Level=1 or Level=2)]]"
set "LOG_NAME=System"
set "TMPFILE=%~dp0tmp_event.txt"
set "LOGFILE=%~dp0event_alert.log"
wevtutil qe %LOG_NAME% /q:"%QUERY%" /f:text /c:10 /rd:true > "%TMPFILE%" 2>&1
for %%F in ("%TMPFILE%") do (
if %%~zF == 0 (
echo [%DATE% %TIME%] エラーなし >> "%LOGFILE%"
del "%TMPFILE%"
exit /b 0
)
)
echo [%DATE% %TIME%] エラー/クリティカルイベントを検出しました。 >> "%LOGFILE%"
type "%TMPFILE%" >> "%LOGFILE%"
del "%TMPFILE%"
endlocal
XPathでのレベル指定まとめ
| Level値 | 意味 |
|---|---|
| 1 | Critical(致命的) |
| 2 | Error(エラー) |
| 3 | Warning(警告) |
| 4 | Information(情報) |
| 0 | LogAlways(常にログ) |
時間範囲を指定して直近のエラーだけを検出する
定期的にバッチを実行するとき、「前回実行から今回までの間に発生したエラー」だけを拾いたいケースがあります。XPathの TimeCreated を使って時間範囲を絞れます。
@echo off
setlocal
:: 現在時刻から過去1時間分のミリ秒(1時間 = 3600000ms)
set "MILLISECONDS=3600000"
set "LOG_NAME=System"
set "TMPFILE=%~dp0tmp_event.txt"
set "LOGFILE=%~dp0event_alert.log"
:: TimeCreated[@SystemTime >= now - N ms] を利用
set "QUERY=*[System[(Level=1 or Level=2) and TimeCreated[timediff(@SystemTime) <= %MILLISECONDS%]]]"
wevtutil qe %LOG_NAME% /q:"%QUERY%" /f:text /c:50 /rd:true > "%TMPFILE%" 2>&1
for %%F in ("%TMPFILE%") do (
if %%~zF == 0 (
echo [%DATE% %TIME%] 過去1時間のエラーなし >> "%LOGFILE%"
del "%TMPFILE%"
exit /b 0
)
)
echo [%DATE% %TIME%] 過去1時間にエラーを検出しました。 >> "%LOGFILE%"
type "%TMPFILE%" >> "%LOGFILE%"
echo ======================================== >> "%LOGFILE%"
del "%TMPFILE%"
endlocal
ポイント:timediff で相対時間指定
timediff(@SystemTime) は現在時刻とイベント発生時刻の差をミリ秒で返します。これを使うと「過去○時間以内」という相対指定が簡単に書けます。1時間=3,600,000ms、24時間=86,400,000ms として計算してください。
検出時の通知方法
エラーを検出した後の通知処理をいくつか紹介します。運用環境に合わせて選んでください。
方法1:画面にポップアップを表示する(msg コマンド)
:: ログオン中のユーザーにポップアップを送る msg * /time:30 "イベントログでエラーを検出しました。詳細は event_alert.log を確認してください。"
注意:msg の制限
msg はターミナルサービスが有効な環境でのみ動作します。タスクスケジューラ経由での実行(バックグラウンド)では表示されません。あくまで「現在ログオンしているユーザーへの通知」として使いましょう。
方法2:カスタムイベントログに書き込む(eventcreate)
:: 管理者権限が必要 eventcreate /t ERROR /id 200 /l APPLICATION /d "エラーを検出: EventID 7036 が System ログに記録されました"
この方法はイベントビューアーで一元管理でき、監視ツール(Zabbix等)との連携にも向いています。
方法3:PowerShell経由でメール通知する
param($message)
$smtpServer = "smtp.example.com"
$from = "monitor@example.com"
$to = "admin@example.com"
$subject = "[警告] Windowsイベントログでエラーを検出"
Send-MailMessage -SmtpServer $smtpServer `
-From $from `
-To $to `
-Subject $subject `
-Body $message `
-Encoding UTF8
:: バッチ内からPowerShellを呼び出して通知 powershell -ExecutionPolicy Bypass -File "%~dp0notify_mail.ps1" -message "EventID 7036 を検出しました。"
PowerShellとバッチの連携についてはPowerShellをバッチファイルから呼び出す方法で詳しく解説しています。
タスクスケジューラで定期監視を自動化する
バッチファイルをタスクスケジューラに登録すれば、手動実行なしで定期的にイベントログを監視できます。
@echo off
:: check_event.bat を1時間ごとに実行するタスクを登録
schtasks /create ^
/tn "EventLogMonitor" ^
/tr "\"C:\tasks\check_event.bat\"" ^
/sc hourly ^
/mo 1 ^
/ru SYSTEM ^
/rl HIGHEST ^
/f
echo タスクを登録しました。
タスクスケジューラへのバッチ登録の詳細はバッチでタスクスケジューラを登録・削除する方法を参照してください。
ポイント:/ru SYSTEM /rl HIGHEST について
SYSTEMアカウントで最上位の特権で実行することで、Securityログへのアクセスやeventcreate のような管理者権限が必要な操作も正常に動作します。ただし、msg コマンドのポップアップはSYSTEM実行では表示されません。
完全版:定期監視バッチスクリプト
ここまでの内容を組み合わせた、実務で使える完全版スクリプトです。過去1時間のエラー/クリティカルイベントを検出し、ログ記録・PowerShell通知・カスタムイベント書き込みを行います。
@echo off
setlocal
:: ===== 設定 =====
set "LOG_NAME=System"
set "MILLISECONDS=3600000"
set "TMPFILE=%~dp0tmp_event.txt"
set "LOGFILE=%~dp0event_monitor.log"
set "PSNOTIFY=%~dp0notify_mail.ps1"
set "QUERY=*[System[(Level=1 or Level=2) and TimeCreated[timediff(@SystemTime) <= %MILLISECONDS%]]]"
echo ==== 監視開始 %DATE% %TIME% ==== >> "%LOGFILE%"
:: ===== イベント取得 =====
wevtutil qe %LOG_NAME% /q:"%QUERY%" /f:text /c:50 /rd:true > "%TMPFILE%" 2>&1
:: ===== 検出チェック =====
for %%F in ("%TMPFILE%") do (
if %%~zF == 0 (
echo [INFO] 過去1時間のエラーなし。 >> "%LOGFILE%"
del "%TMPFILE%"
echo ==== 監視終了 %DATE% %TIME% ==== >> "%LOGFILE%"
exit /b 0
)
)
:: ===== エラー検出時の処理 =====
echo [ALERT] 過去1時間にエラーを検出しました。 >> "%LOGFILE%"
type "%TMPFILE%" >> "%LOGFILE%"
echo ======================================== >> "%LOGFILE%"
:: PowerShellでメール通知(スクリプトがある場合のみ)
if exist "%PSNOTIFY%" (
powershell -ExecutionPolicy Bypass -File "%PSNOTIFY%" -message "Systemログでエラーを検出。%LOGFILE% を確認してください。"
)
:: カスタムイベントログにも記録
eventcreate /t ERROR /id 200 /l APPLICATION /d "event_monitor: Systemログのエラーを検出" >nul 2>&1
del "%TMPFILE%"
echo ==== 監視終了 %DATE% %TIME% ==== >> "%LOGFILE%"
endlocal
よく監視されるEventIDまとめ
実務でよく監視対象となるEventIDを種類別にまとめます。
| EventID | チャンネル | 内容 |
|---|---|---|
| 41 | System | 予期しないシャットダウン(電源断・クラッシュ) |
| 1001 | Application | Windowsエラー報告(アプリクラッシュ) |
| 1074 | System | ユーザーによるシャットダウン・再起動 |
| 6005 | System | イベントログサービス開始(システム起動) |
| 6006 | System | イベントログサービス停止(正常シャットダウン) |
| 6008 | System | 予期しないシャットダウン |
| 7034 | System | サービスが予期せず終了 |
| 7036 | System | サービスの開始・停止 |
| 4625 | Security | ログオン失敗(不正アクセス検知に利用) |
| 4647 | Security | ユーザーによるログオフ |
| 4720 | Security | ユーザーアカウントの作成 |
| 11 | System | ディスクエラー(disk ソース) |
EventIDの調べ方
イベントビューアー(eventvwr.msc)でイベントをダブルクリックすると詳細が確認できます。「全般」タブの末尾にある「このオンラインのヘルプとサポートの詳細情報」リンクからMicrosoftの解説ページに飛ぶこともできます。
よくある質問(FAQ)
wevtutil で何も返ってこない場合の原因は?wevtutil qe System /c:3 /f:text /rd:true のように条件なしで動作確認してから絞り込むと原因を特定しやすくなります。*[System[...]] の *(ワイルドカード)は必須です。また、文字列の引用符はXPath内でシングルクォート(')を使い、バッチのダブルクォートと混在させないよう注意してください。timediff を使った時間絞り込みが最もシンプルな対策です。タスクを1時間ごとに実行するなら timediff(@SystemTime) <= 3600000 で直近1時間分だけ取得することで、前回検出済みのイベントを除外できます。event_log_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log)か、ローテーション処理を追加するのがおすすめです。詳しくはバッチファイルでエラーログを自動収集して保存する方法を参照してください。まとめ
バッチファイルと wevtutil を組み合わせることで、Windowsイベントログの特定エラーを自動検出して通知する仕組みを構築できます。
- 基本検索:
wevtutil qe <ログ名> /q:"XPathクエリ" - 複数EventID:XPathで
EventID=A or EventID=Bと記述 - レベル絞り込み:
Level=1 or Level=2でError/Critical - 時間範囲:
timediff(@SystemTime) <= ミリ秒で直近N時間 - 通知方法:ファイル記録・
eventcreate・PowerShellメール送信 - 自動化:タスクスケジューラで定期実行(SYSTEMアカウント+最上位特権)
エラーログの収集・保存についてはバッチファイルでエラーログを自動収集して保存する方法、エラー検知後の処理終了パターンについてはエラー時に処理を中断・終了する方法もあわせて参考にしてください。

