Monthly Archives: November 2025

Improving Event Log Filtering in PowerShell

Recently, one of our customers asked us to help port their PowerShell script to our script engine (built on FastScript/Pascal Script). While helping them, we paid close attention to how they filtered events in PowerShell—and noticed a common performance pitfall.

Their original script filtered the System log by event type using a pipeline:

$elSysErr = Get-EventLog -LogName System | Where-Object EntryType -Eq 'Error'

Although the PowerShell pipeline is a powerful feature, using it here is unnecessary and inefficient. This command first loads all events from the System log into memory and only then filters them by type. A more efficient approach is to filter during retrieval:

$elSysErr = Get-EventLog -LogName System -EntryType 'Error'

This runs faster—but there’s a bigger issue.
You shouldn’t use Get-EventLog at all:

  1. Its filtering parameters are limited (no reliable way to filter by Event ID).
  2. It works only with classic/legacy logs.
  3. It relies on deprecated Windows APIs.

Microsoft now recommends using Get-WinEvent, which is documented here:
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.diagnostics/get-winevent

Rewriting the Command with Get-WinEvent

At first glance, you might simply replace Get-EventLog with Get-WinEvent:

$elSysErr = Get-WinEvent System | Where-Object LevelDisplayName -eq 'Error'

Or slightly better:

$elSysErr = Get-WinEvent System | Where-Object Level -eq 2

But this still requires reading the entire log first—so you gain no performance benefits.

The real power of Get-WinEvent is that it allows event filtering before reading the log. You can filter in two ways:

  • Using a hashtable
  • Using an XML (XPath) query

Using a Hashtable (the simplest approach)

$elSysErr = Get-WinEvent -FilterHashTable @{LogName='System'; Level=2}

Using an XPath query

$elSysErr = Get-WinEvent -LogName 'System' -FilterXPath '*[System[(Level=2)]]'

Performance Comparison

I ran all variations against my local System log (≈50,000 events), using Measure-Command.

Pipeline filtering (Get-EventLog + Where-Object)

Measure-Command { Get-EventLog -LogName System | Where-Object EntryType -Eq 'Error' }

≈ 8 seconds

Filter parameter (Get-EventLog -EntryType)

Measure-Command { Get-EventLog -LogName System -EntryType 'Error' }

≈ 6 seconds (about 25% faster)

Pipeline filtering with Get-WinEvent

Measure-Command { Get-WinEvent System | Where-Object Level -eq 2 }

≈ 13 seconds (even slower!)

FilterHashTable (the fastest)

Measure-Command { Get-WinEvent -FilterHashTable @{LogName='System'; Level=2} }

≈ 566 ms

XPath filter

Measure-Command { Get-WinEvent -LogName 'System' -FilterXPath '*[System[(Level=2)]]' }

≈ 600 ms

Conclusion:

For performance, always use Get-WinEvent with -FilterHashTable, -FilterXML, or -FilterXPath.

Handling the “No events found” error

One important detail rarely mentioned in online resources:
If no events match the filter, Get-WinEvent throws a non-terminating error.

This is not a big issue in interactive sessions, but in scripts it produces noisy output. To avoid this, simply ignore the error:

$elSysErr = Get-WinEvent -FilterHashTable @{LogName='System'; Level=2} -ErrorAction SilentlyContinue
if ($elSysErr -eq $null) {
    Write-Output "no events match this filter"
} else {
    # work with the result set
}

How This Looks in Event Log Explorer Script Engine

Here’s how we port the same query into our scripting engine:

theApp.OpenLogQuery('', 'System', '*[System[(Level = 2)]]', True);

To measure performance:

var
  StartTime, FinishTime: DWORD;
begin
  StartTime := GetTickCount();
  theApp.OpenLogQuery('', 'System', '*[System[(Level = 2)]]', True);
  FinishTime := GetTickCount();
  DebugOut('Executed in ', FinishTime - StartTime, ' ms');
end.

Result: ≈ 609 ms
Not bad—especially considering it not only retrieves the events, but also displays them.

Closing Thoughts

That’s all for now. As the developers of Event Log Explorer, we strongly recommend using our software for tasks involving event log analysis. However, we fully recognize that PowerShell offers an exceptionally powerful scripting environment for managing Windows systems. When you choose to work with PowerShell, it’s important to do so efficiently—and using the right event filtering techniques can dramatically improve performance.

Facebooktwitterredditpinterestlinkedinmail