Get-WinEvent. Filter at the log with -FilterHashtable, not afterwards with Where-Object, or you pull the whole log into memory first.
Reading Windows event logs from PowerShell is one of those jobs where the obvious approach works fine on a small log and crawls on a real one. This covers the cmdlet to use, how to filter properly, and the older cmdlet you will still see in examples.
Listing The Logs
There are hundreds of logs on a modern Windows machine, most of them empty. Show the ones that actually have records in them:
Get-WinEvent -ListLog * |
Where-Object { $_.RecordCount -gt 0 } |
Sort-Object RecordCount -Descending |
Select-Object LogName, RecordCount -First 10
LogName RecordCount ------- ----------- Application 56312 System 39418 Microsoft-Windows-StorageManagement/Operational 7214 Security 4180
Application, System and Security are the three classic logs. The long Microsoft-Windows-* names are per-component logs, and they are where the useful detail usually lives once you know which component you care about.
Reading Recent Events
Get-WinEvent -LogName System -MaxEvents 20
-MaxEvents is not optional in practice. Without it you are asking for tens of thousands of records.
Filtering Properly
This is the part that matters. There are two ways to filter and they perform very differently.
The slow way
# pulls the ENTIRE log into memory, then discards most of it
Get-WinEvent -LogName System | Where-Object { $_.Level -eq 2 }
The fast way
Get-WinEvent -FilterHashtable @{
LogName = 'System'
Level = 2, 3 # 2 = Error, 3 = Warning
StartTime = (Get-Date).AddDays(-7)
} -MaxEvents 50
-FilterHashtable pushes the filter down to the event log service, so only matching records are ever returned. On a log with tens of thousands of records that is the difference between a command you wait for and one you do not.
The level numbers are the ones to memorise: 1 Critical, 2 Error, 3 Warning, 4 Information, 5 Verbose. They are numbers in the filter and words in the output, which is mildly annoying and worth expecting.
Useful Filters
# errors and warnings from the last day
Get-WinEvent -FilterHashtable @{ LogName='System'; Level=1,2,3
StartTime=(Get-Date).AddDays(-1) }
# one specific event ID
Get-WinEvent -FilterHashtable @{ LogName='System'; ID=6008 }
# everything from one source
Get-WinEvent -FilterHashtable @{ LogName='Application'; ProviderName='MsiInstaller' }
# unexpected shutdowns, tidied for reading
Get-WinEvent -FilterHashtable @{ LogName='System'; ID=6008 } -MaxEvents 10 |
Select-Object TimeCreated, Id, LevelDisplayName, Message |
Format-List
Get-WinEvent -FilterHashtable raises “No events were found that match the specified selection criteria” as an error rather than returning nothing. In a script, add -ErrorAction SilentlyContinue so a quiet log does not read as a failure.Reading A Remote Machine
Get-WinEvent -ComputerName SERVER01 -FilterHashtable @{
LogName='System'; Level=1,2 } -MaxEvents 25
This needs the Remote Event Log Management firewall rule enabled on the target and appropriate permissions. The Security log additionally requires administrator rights, locally or remotely.
What About Get-EventLog?
You will find plenty of older examples using Get-EventLog:
Get-EventLog -LogName System -Newest 20
It still runs, including on PowerShell 7. It only reads the classic logs though, so it cannot see any of the Microsoft-Windows-* ones, and it has no equivalent of -FilterHashtable. Treat it as legacy: fine in an old script you are not touching, not what you would write today. Get-WinEvent does everything it does and more.
If SQL Server is part of your day job, my current work lives over at sqldba.blog: production DBA scripts, an error library and the SSMS guide.
Leave a Reply