PowershellWindows

Windowsのイベントログを絞り込む【Get-WinEventで186倍速くなる】

スポンサーラベル
イベントログをPowerShellで絞り込むときの4つの注意点。Get-EventLogでは見えないログ、Where-Objectでの絞り込みが186倍遅い、0件がエラー扱い、Messageが空になるイベント Powershell

当サイトはアフィリエイト広告を利用しています。

サーバの調子がおかしいとき、まずイベントビューアーを開きます。ただ、あの画面で目的のログを探すのは骨が折れます。スクロールしても終わらないし、フィルターのダイアログは項目が多すぎます。

PowerShell なら1行で取れます。ただし絞り方を間違えると、同じ結果を得るのに186倍の時間がかかります。さらに、0件だったときにエラーが出たり、本文が空のまま返ってきたりします。

イベントログをPowerShellで絞り込むときの4つの注意点。Get-EventLogでは見えないログ、Where-Objectでの絞り込みが186倍遅い、0件がエラー扱い、Messageが空になるイベント

この記事では、実際に計測した結果をもとに、詰まりやすい4点を整理します。検証は Windows 11 Pro・Windows PowerShell 5.1・PowerShell 7.4 で行いました。

1. 結論:Get-WinEvent に -FilterHashtable を渡す

先に答えです。

# System ログから、直近24時間の「エラー」だけを取る
Get-WinEvent -FilterHashtable @{
    LogName   = 'System'
    Level     = 2                       # 2=エラー 3=警告 4=情報
    StartTime = (Get-Date).AddDays(-1)
} -ErrorAction SilentlyContinue |
    Select-Object TimeCreated, Id, ProviderName, Message

覚えることは3つだけです。

  • Get-EventLog ではなく Get-WinEvent を使う
  • Where-Object ではなく -FilterHashtable で絞る
  • -ErrorAction SilentlyContinue を付ける

なぜこの3つなのかを、順に見ていきます。

2. Get-EventLog では11個のログしか見えない

PowerShell でイベントログを扱うコマンドレットは2つあります。どちらも現行の PowerShell 7.4 に残っているので、どちらでもよさそうに見えます。

しかし見えている範囲がまったく違います。検証機で数えた結果です。

コマンド見えるログの数
Get-EventLog -List11
Get-WinEvent -ListLog *520(うち記録があるもの132)

Get-EventLog で見えるのは、Application / System / Security といったクラシックログだけです。イベントビューアーの「アプリケーションとサービス ログ」の下にある Microsoft-Windows-〜/Operational 系は1つも見えません。

実際に開こうとすると、こう返ります。

Get-EventLog -LogName 'Microsoft-Windows-WindowsUpdateClient/Operational' -Newest 1
# → コンピューター '.' のイベント ログ 'Microsoft-Windows-WindowsUpdateClient/Operational' は存在しません。

「存在しません」と言われますが、ログは存在します。 同じログを Get-WinEvent で開けば普通に読めます。

Get-WinEvent -LogName 'Microsoft-Windows-WindowsUpdateClient/Operational' -MaxEvents 1

Windows Update、タスクスケジューラ、リモートデスクトップ、グループポリシー。業務で本当に見たいログはだいたいこちら側にあります。ここで Get-EventLog を使っていると、「ログが無いらしい」と誤解したまま調査が止まります。

Get-WinEvent だけ覚えれば足ります。

3. Where-Object で絞ると186倍遅い

ここが一番効きます。同じ結果を返す2つの書き方を比べます。

# 遅い書き方:全部読んでから捨てる
Get-WinEvent -LogName System | Where-Object Id -eq 7036

# 速い書き方:読む前に絞る
Get-WinEvent -FilterHashtable @{LogName='System'; Id=7036} -ErrorAction SilentlyContinue

Measure-Command で計測しました。System ログに30,948件が入っている状態です。

書き方取得件数所要時間
Where-Object で絞る140件24.95秒
-FilterHashtable で絞る140件0.13秒

186倍の差が付きました。 結果は1件も違いません。

理由は単純で、Where-Object30,948件すべてをオブジェクトに変換してから140件を選んでいるのに対し、-FilterHashtableイベントログ側に条件を渡して140件だけ受け取っているからです。

PowerShell の他の場面では「パイプで繋いで Where-Object」で困らないので、つい同じ癖で書いてしまいます。イベントログではここだけ別扱いにしてください。

StartTime での絞り込みも同じくらい速く、直近24時間の728件を0.83秒で取れました。

Get-WinEvent -FilterHashtable @{
    LogName   = 'System'
    StartTime = (Get-Date).AddDays(-1)
} -ErrorAction SilentlyContinue

-FilterHashtable で使えるキー

よく使うのはこのあたりです。複数書くと AND 条件になります。

キー指定する内容
LogNameログ名(System Application Microsoft-Windows-〜/Operational
IdイベントID。配列で複数指定できる
Level1=重大 2=エラー 3=警告 4=情報
ProviderNameソース名(Service Control Manager など)
StartTime / EndTime期間

4. 0件だったときに「エラー」が出る

-FilterHashtable で条件に合うイベントが1件も無いと、PowerShell は赤字のエラーを出します

Get-WinEvent -FilterHashtable @{LogName='System'; Id=999999}
# → 指定した選択条件に一致するイベントは見つかりませんでした。

カテゴリは ObjectNotFound です。0件は正常な結果であって異常ではないのですが、コマンドレットはエラーとして扱います。

対話的に叩いているぶんには「無かったのね」で済みます。困るのはスクリプトに組み込んだときです。

  • ログに赤字が残り、後から見た人が「何か壊れている」と誤解する
  • $ErrorActionPreference = 'Stop' を設定したスクリプトでは、そこで処理が止まる
  • try/catch を書いていると、0件のたびに例外処理に落ちる

対処は -ErrorAction SilentlyContinue を付けるだけです。

$events = @(Get-WinEvent -FilterHashtable @{LogName='System'; Id=999999} -ErrorAction SilentlyContinue)
if ($events.Count -eq 0) { "該当なし" }

@() で囲っているのは、0件のときに $null が返るためです。 $null.Count を付けると PowerShell 5.1 では何も返らず、件数判定が効きません。配列にキャストしてから数えるのが安全です。

5. Message が空のまま返ってくることがある

イベントログで一番読みたいのは Message です。ところが、ここが空になるイベントが混ざります

検証機で System ログの直近200件を調べたところ、5件の Message が空でした。いずれも同じプロバイダ(ネットワークアダプタのドライバ)が出したものです。

$events = Get-WinEvent -FilterHashtable @{LogName='System'} -MaxEvents 200 -ErrorAction SilentlyContinue
$events | Where-Object { [string]::IsNullOrWhiteSpace($_.Message) } |
    Group-Object ProviderName | Select-Object Count, Name

原因は、イベントの文面を組み立てるためのメッセージ定義がそのPCで読めないことです。プロバイダがレジストリに登録されていても、参照先のファイルが無い・壊れている・別アーキテクチャ向けといった場合に起こります。ドライバや監視エージェントを入れ替えたPCで出やすくなります。

問題は、エラーが出ないことです。 Message が空の文字列として返ってくるだけなので、CSVに落とすと「その行だけ説明が空欄」になり、件数は合っているので気づけません。

空でも情報は残っています

Message が組み立てられないだけで、元データはイベントの中に残っていますToXml() で取り出せます。

$e = $events | Where-Object { [string]::IsNullOrWhiteSpace($_.Message) } | Select-Object -First 1
([xml]$e.ToXml()).Event.EventData.Data | ForEach-Object { "{0} = {1}" -f $_.Name, $_.'#text' }

実際にこれで、空だったイベントから対象デバイス名を取り出せました。Message が空でも諦めずに XML を見てください。

6. イベントの中身(EventData)で絞り込む

-FilterHashtable はイベントIDやレベルまでしか絞れません。「特定のサービスに関するものだけ」のように中身で絞りたいときは -FilterXPath を使います。

たとえばサービスの状態変化(Service Control Manager のイベントID 7040)は、こういう構造を持っています。

param1 = Background Intelligent Transfer Service
param2 = 自動的な開始
param3 = 要求による開始
param4 = BITS

この param1 を条件にして絞ります。

$xpath = "*[EventData[Data[@Name='param1']='Background Intelligent Transfer Service']]"
Get-WinEvent -LogName System -FilterXPath $xpath -MaxEvents 100 -ErrorAction SilentlyContinue

100件を 0.07秒で取れました。-FilterHashtable と同じくログ側で絞っているので速いままです。

構造が分からないときは、まず1件取って XML を眺めるのが近道です。

$e = Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='Service Control Manager'} -MaxEvents 1
([xml]$e.ToXml()).Event.EventData.Data | Format-Table Name, '#text'

7. 権限とログの前提を先に確認する

調査を始める前に、2つだけ確認しておくと無駄足が減ります。

Security ログは管理者権限が必要です。 一般ユーザーで開くとこう返ります。

許可されていない操作を実行しようとしました。

「イベントが無い」とは言われません。 アクセスできないことが分かりにくい文面なので、Security ログを見るときは最初から管理者で PowerShell を起動してください。

そのログが有効かどうかも確認します。 記録が無効なログは、いくら検索しても0件です。

Get-WinEvent -ListLog 'Microsoft-Windows-TaskScheduler/Operational' |
    Select-Object LogName, IsEnabled, RecordCount, MaximumSizeInBytes

検証機では、このタスクスケジューラのログが IsEnabled = False でした。既定で無効なログは実際にあります。 「イベントが出ていない」のか「記録していない」のかは、まったく別の話です。

このログを有効にする手順と、タスクが動かないときの切り分けはタスクスケジューラで組んだ処理が動かないときの切り分けにまとめました。

8. まとめ

イベントログを PowerShell で絞り込むときの要点です。

  • Get-WinEvent を使う。 Get-EventLog では11ログしか見えず、Microsoft-Windows-〜/Operational 系が丸ごと見えない
  • -FilterHashtable で絞る。 Where-Object との差は実測で 186倍(24.95秒 → 0.13秒)
  • -ErrorAction SilentlyContinue を付ける。 0件はエラー扱いになる。件数判定は @() で囲ってから
  • Message が空のイベントが混ざる。 エラーは出ない。ToXml() を見れば元データは残っている
  • 中身で絞るなら -FilterXPathSecurity は管理者権限、無効なログは常に0件

最初に覚えるのは -FilterHashtable の形だけで十分です。全部読んでから捨てる書き方をやめるだけで、調査の待ち時間がほぼ無くなります。

時刻同期のように「どこで止まっているか分からない」タイプの調査は、イベントログの読み方とセットで手順にしておくと早く終わります。手順はWindowsの時刻同期がずれるときの切り分け手順にまとめてあります。

取得した結果を CSV に落とすときは、文字コードと件数の欠けに注意してください。こちらはPowerShellでファイル一覧をCSVに出すで実測しています。

PowerShellの学習におすすめの書籍

PowerShell を体系的に学ぶなら、PowerShell Core 以降のクロスプラットフォーム対応をふまえた解説書が分かりやすいです。Windows の自動化にとどまらず、macOS や Linux でも動く現在の PowerShell を、基礎から実務での使い方まで通して押さえられます。

PowerShell実践ガイドブック クロスプラットフォーム対応の次世代シェルを徹底解説

コメント

タイトルとURLをコピーしました