Show Event Logs in PowerShell

📜Part of the PowerShell Complete Guide, every PowerShell post on this site, grouped by the job you are doing.
The short answer

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
An empty result is not always an empty log. If no records match, 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.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Looking for SQL Server?

This site started as a SQL Server blog in 2017 and the archive is still here. The new SQL Server writing, and all the scripts, moved to a site of their own.

sqldba.blogScripts, error library, wait types, SSMS
Browse by topic