経緯
PowerShell で Windows のイベントビューアで閲覧できるイベントログのうち、
- イベントログ
- ソースID
- レベル
- 時間
で絞り込んで走査したくなったので、やってみました。
成果物 (スニペット)
関数
##
# Find-WinEventLogSpecified: Windows のイベントログから、指定されたログ名、ソースID、レベル、日時範囲に合致する最新のログのオブジェクトから生成した Hashtable を返却する
#
# @param {String} $logName: 検索するログのログ名
# @param {Int} $id: 検索するログのソースID
# @param {Int} $level: 検索するログのレベル
# @param {Int} $addMinutes: 検索するログの日時範囲(*分前)
#
# @return {EventLogRecord} : 指定されたログ名、ソースID、レベル、日時範囲に合致する最新のログのオブジェクトを返却する
#
function Find-WinEventLogSpecified([String]$logName, [Int]$id, [Int]$level, [Int]$addMinutes) {
# $ErrorActionPreference: 終了しないエラーの取り扱いに関するパラメータ。
# デフォルト: Continue -> エラーメッセージが表示されるが例外は発生せずに処理継続。
# Stop とすることで、例外を発生させる。
# ただし、単純にイベントログがなかった場合 ($category -eq "ObjectNotFound") は正常として処理継続させる。
$ErrorActionPreference = 'Stop'
try {
$eventObj = (Get-WinEvent -Filterhashtable @{LogName=$logName;id=$id;Level=$level;starttime=(Get-Date).AddMinutes($addMinutes);endtime=(Get-Date)})
if($eventObj.Length -gt 0) {
$logHashTable = [Hashtable]@{
'datetime' = $eventObj[0].TimeCreated;
'id' = $eventObj[0].Id;
'level' = $eventObj[0].LevelDisplayName;
'message' = $eventObj[0].Message;
}
# 最新のログを返却
return $logHashTable
}
else {
throw '該当するログが存在しませんでした。'
}
}
catch {
$category = $Global:Error[0].CategoryInfo.Category.ToString()
if ($category -eq 'ObjectNotFound' -or $_.Exception.Message -eq '該当するログが存在しませんでした。') {
# ログがなかっただけ。問題ない。
$emptyHashTable = [Hashtable]@{
'datetime' = Get-Date -UFormat "%Y/%m/%d %H:%M:%S";
'id' = -999;
'level' = '情報';
'message' = '該当するログが存在しませんでした。';
}
return $emptyHashTable
} else {
Show-ErrorMessage 51 $True $_.Exception.Message $logPath
}
}
}
使用時
# アプリケーションログの中でソースIDが 1003 、情報レベルで過去1時間以内のログを走査
$logHashtable = Find-WinEventLogSpecified 'Application' 1003 4 -60
if($logHashtable.id -ne -999) {
Write-Output '条件に合致するログが存在します。'
}
else {
Write-Output '条件に合致するログは存在しません。'
}
ざっくりこのような感じで実装しました。
備考
処理の実装自体は Get-WinEvent + Filterhashtable でわりとサクッとできたのですが、問題は上演に合致するイベントログが存在しないケース。
試しに PowerShell を起動してワンライナーで確認すると、以下のようになります。
> (Get-WinEvent -Filterhashtable @{LogName='Application';id=1003;Level=4;starttime=(Get-Date).AddMinutes(-9);endtime=(Get-Date)})
Get-WinEvent : 指定した選択条件に一致するイベントが見つかりませんでした。
発生場所 行:1 文字:2
+ (Get-WinEvent -Filterhashtable @{LogName='Application';id=1003;Level= ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (:) [Get-WinEvent], Exception
+ FullyQualifiedErrorId : NoMatchingEventsFound,Microsoft.PowerShell.Commands.GetWinEventCommand
標準出力に赤字でイベントが見付からなかったエラーが表示されてしまいます。この表示がされてしまうと驚いてしまうので潰しておきたい。
そこで try { ... } catch { ... }
構文でエラーハンドリングしようと考えたのですが、 PowerShell のこの手のエラー発生時に停止しないで進んでしまうエラーの場合、そのままでは catch
で捕まえられないとのこと。
この挙動は変数 $ErrorActionPreference
によって決まり、デフォルトでは Continue
。これを $ErrorActionPreference = "Stop"
で Stop
に書き換えることでエラーハンドリングできるようになる、ということで一行追加しています。
ただしこれによってイベントログが存在しないだけでも catch
文に入るので、さらに条件分岐でイベントログがない場合は正常として軌道修正……という調整を。
この記事がないと途方に暮れるところでした。この場を借りて感謝を申し上げます。
参考
Get-WinEvent + FilterHashtable
- Get-WinEvent (Microsoft.PowerShell.Diagnostics) – PowerShell | Microsoft Learn
- FilterHashtable を使った Get-WinEvent クエリの作成 – PowerShell | Microsoft Learn
サンプルコード
停止しないエラーへの対処 (例外を投げてキャッチさせる)
- PowerShellでイベントログを取得する時、「指定した選択条件に一致するイベントが見つかりませんでした。」が煩わしいのでcatchする
- PowerShell のエラーハンドリングを(今度こそ)理解する #PowerShell – Qiita