【bat】Windowsイベントログから特定のエラーを検出して自動通知する方法|wevtutil・XPathクエリ・タスクスケジューラ連携まで完全解説

【bat】Windowsイベントログから特定のエラーを検出して自動通知する方法|wevtutil・XPathクエリ・タスクスケジューラ連携まで完全解説 bat

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 最新から順に取得(デフォルトは古い順)
システムログからEventID 7036(サービス停止)を1件取得
wevtutil qe System /q:"*[System[(EventID=7036)]]" /f:text /c:1 /rd:true

注意:管理者権限が必要なケースがあります

Securityログは管理者権限がないと読み取れません。System・Application ログは通常ユーザーでも読み取れますが、タスクスケジューラで実行する場合は「最上位の特権で実行」にチェックを入れておくと安全です。

特定のEventIDを検出してログに記録する(基本スクリプト)

まず最もシンプルなパターンとして、特定のEventIDが存在するかを確認し、見つかればログファイルに記録するスクリプトです。

check_event.bat(基本版)
@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を同時に監視できます。

複数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を指定せず、「エラーレベル以上のイベントをすべて監視する」といった使い方もできます。

エラー(Level=2)以上のイベントを取得
@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 を使って時間範囲を絞れます。

過去1時間以内のエラーを検出
@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経由でメール通知する

notify_mail.ps1(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を呼び出して通知
powershell -ExecutionPolicy Bypass -File "%~dp0notify_mail.ps1" -message "EventID 7036 を検出しました。"

PowerShellとバッチの連携についてはPowerShellをバッチファイルから呼び出す方法で詳しく解説しています。

タスクスケジューラで定期監視を自動化する

バッチファイルをタスクスケジューラに登録すれば、手動実行なしで定期的にイベントログを監視できます。

タスクスケジューラへの登録(1時間ごと)
@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通知・カスタムイベント書き込みを行います。

event_monitor.bat(完全版)
@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 で何も返ってこない場合の原因は?
主な原因は(1)指定したEventIDのイベントが存在しない、(2)XPathクエリの構文ミス、(3)権限不足(Securityログなど)の3つです。まず wevtutil qe System /c:3 /f:text /rd:true のように条件なしで動作確認してから絞り込むと原因を特定しやすくなります。
XPathクエリの構文を間違えやすい点は?
*[System[...]]*(ワイルドカード)は必須です。また、文字列の引用符はXPath内でシングルクォート(')を使い、バッチのダブルクォートと混在させないよう注意してください。
Securityログを読み取ろうとしたらアクセス拒否になる
Securityログは管理者権限が必要です。コマンドプロンプトを「管理者として実行」するか、タスクスケジューラで「最上位の特権で実行」を有効にしてください。
毎回同じイベントを重複検出してしまう
timediff を使った時間絞り込みが最もシンプルな対策です。タスクを1時間ごとに実行するなら timediff(@SystemTime) <= 3600000 で直近1時間分だけ取得することで、前回検出済みのイベントを除外できます。
エラーログを別ファイルに残したい・ログが肥大化するのが気になる
日付をログファイル名に含める(例:event_log_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log)か、ローテーション処理を追加するのがおすすめです。詳しくはバッチファイルでエラーログを自動収集して保存する方法を参照してください。
ERRORLEVELで検出する方法との違いは?
ERRORLEVELはバッチ内で実行したコマンドの成否を返すものであり、システム全体のイベントログとは独立しています。ERRORLEVELによるエラー検知はERRORLEVEL以外でエラーを検知する10の方法で詳しく解説しています。

まとめ

バッチファイルと 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アカウント+最上位特権)

エラーログの収集・保存についてはバッチファイルでエラーログを自動収集して保存する方法、エラー検知後の処理終了パターンについてはエラー時に処理を中断・終了する方法もあわせて参考にしてください。