PowerShell: Get-WinEvent vs. Get-EventLog

I’ve been working to write a flexible PowerShell script to retrieve and email warning and error events from computers in a small network. The computers variously run XP, Windows 7, Server 2003, SBS 2008, and Server 2008 R2. I wanted to include the new Applications and Services logs for Vista/2008 and beyond, so for those OSs, I use the new Get-WinEvent cmdlet that is part of PowerShell 2.0.

Resources

I found three primary Microsoft articles on Get-WinEvent:

Differences from Get-EventLog

The good new is that Get-WinEvent does allow retrieving events from the Applications and Services logs. Unlike Get-EventLog, which returns System.Diagnostics.EventLogEntry, Get-WinEvent returns System.Diagnostics.Eventing.Reader.EventLogRecord. There are significant differences in the properties:

  • Source becomes ProviderName
  • EntryType becomes LevelDisplayName
  • Category becomes TaskDisplayName
  • ReplacementStrings are only visible if you save the event as XML and retrieve the Data:
    $eventxml = $event.ToXML()
    $eventxml.Event.EventData.Data

The Performance Promise

Besides accessing Application event logs, the big advantage of Get-WinEvent is supposed to be performance, especially when retrieving logs from remote computers. If you use one of the Filter parameters, the event engine does the filtering before passing the events (potentially across the network) back to PowerShell.

This is where things really got interesting.

FilterHashtable Fails on Server 2008

Arguably the simplest of the Filter parameters is FilterHashtable. The Scripting Guy article has extensive examples on how to use this. And it works fine on Windows 7 and Server 2008 R2. The problem is, it doesn’t work at all on Server 2008:  all you get is “The parameter is incorrect”. Reportedly it fails on Vista as well.

This thread led me to this PowerShell bug report, which confirms that this is a known bug in “Crimson” (the new event log technology). On April 10, 2009, the PowerShell team said they were following up with the “owners” of Crimson; the bug was later closed as External.

I only wish all the Microsoft articles had included a big highlighted box like this article, warning me not to waste my time on FilterHashtable:

Note  The Get-WinEvent -FilterHashtable parameter works only on Windows 7/2008 R2. On Windows Vista/2008, it generates an error: “The parameter is incorrect.”

Fortunately, there is a workaround:  FilterXML does work on Server 2008; it’s just more of a pain to build up the queries using examples from the Event Viewer’s XML tab.

Filtering by Keyword Slower than Get-EventLog

The main thing I want my script to do is report all the warning and error events in all logs from the past 24 hours. This works pretty well in most logs, and Get-WinEvent beats Get-EventLog nicely, especially over the LAN. For example, here are the elapsed times needed to retrieve 13 events from an SBS 2008 Application log, which is 20MB and contains about 53,000 events (time in h:mm:ss):

Application log:
Warnings and Errors
Time Elapsed – Local Time Elapsed – Remote
Get-EventLog 0:00:16 0:05:34
Get-WinEvent 0:00:05 0:00:27

The Security Log

You may have already noticed that in Vista/Server 2008 and beyond, the Security log considers security failures to be Information events. Actually they are recorded with Event Level 0, LogAlways, which means that “no filtering on the level is done during the event publishing.” (See the StandardEventLevel enumeration.)

This means that even in Event Viewer, if you want to find the security failures, you have to filter by Keyword, not Event Level:

Get-WinEvent 1

With the old PowerShell Get-EventLog cmdlet, to find Audit Failure events, even on Vista and beyond, you use

Where { (“FailureAudit” -eq $_.EntryType) }

With the new PowerShell Get-WinEvent cmdlet, to filter for Audit Failure events, you include this in your FilterXML string:

band(Keywords,4503599627370496)

They both work. The problem is that Get-WinEvent with the “Audit Failure” filter is much slower than Get-EventLog. For example, to retrieve about 24,000 events from an SBS 2008 Security log, which is 128MB and contains about 280,000 events (time in h:mm:ss):

Security log:
Audit Failures
Time Elapsed – Local Time Elapsed – Remote
Get-EventLog 0:01:57 0:30:44
Get-WinEvent 0:09:46 1:04:16

That’s right:  working remotely, Get-WinEvent takes over an hour to retrieve the Audit Failure events, twice as long as Get-EventLog. Running locally, Get-WinEvent takes almost five times as long as the same query run with Get-EventLog.

No doubt the large number of events in the Security log compounds the problem, but that’s only about 27 hours worth of data. By the way, I also ran the tests on a Server 2008 R2 machine. There, Get-EventLog is still faster, but the margin wasn’t as great. However the R2 machine only has about 16,000 events, so it’s not a totally fair comparison.

Conclusions

  1. If you’re writing a PowerShell script to handle events from Vista or Server 2008, avoid the Get-WinEvent –FilterHashtable parameter; use –FilterXML instead.
  2. Even on Vista and beyond, consider using Get-EventLog if you need to filter the Security log for Audit Failures.

The Test Script

If you want to try these tests yourself, here’s the script I used:

EventTest.zip

If you do run the test, leave a comment with your results.

17 thoughts on “PowerShell: Get-WinEvent vs. Get-EventLog

  1. VasekB

    Perfect comaparison. What about “Get-WmiObject Win32_NTLogEvent” ?

  2. VasekB

    Measure-Command -Expression {Get-WmiObject Win32_NTLogEvent -Filter “Logfile=’System’ AND EventCode = ‘7036’”}
    Measure-Command -Expression {Get-WmiObject Win32_NTLogEvent -Filter “Logfile=’System'”}
    TotalSeconds : 5,1125578
    TotalSeconds : 5,1057069
    TotalSecondsAVG : 5,10913235

    Measure-Command -Expression {Get-WinEvent -LogName System | where {$_.id -eq 7036}}
    Measure-Command -Expression {Get-WinEvent -LogName System}
    TotalSeconds : 5,2320771
    TotalSeconds : 4,1938785
    TotalSecondsAVG : 4,7129778

    Measure-Command -Expression {Get-EventLog -LogName System | where {$_.eventID -eq 7036}}
    Measure-Command -Expression {Get-EventLog -LogName System}
    TotalSeconds : 3,8932569
    TotalSeconds : 2,3241086
    TotalSecondsAVG : 3,10868275

    Measure-Command -Expression {Get-EventLog -LogName System -InstanceId 1073748860}
    Measure-Command -Expression {Get-EventLog -LogName System}
    TotalSeconds : 2,1006783
    TotalSeconds : 2,3756022
    TotalSecondsAVG : 2,23814025

  3. Robin

    Hi Mark,

    Are you aware that running the query with -FilterHashTable on a 2008 R2 / Windows 7 machine against a 2008 machine works? At least it does for me when querying remote application and system logs on Server 2008 x86 and x64. I haven’t tried querying the security log though.

    Cheers,

    Robin

  4. Mark Berry Post author

    Robin – good to know, thanks. Kind of makes sense. I wanted my script to run on SBS 2008 (based on Windows Server 2008) so I had to use -FilterXML.

  5. VasekB

    I wrote script for collecting EventLogs from servers to one DB – I had a 3 weeks of fun with them ;o)
    Every of thats command have some kind of problems ;o(

    * Get-WmiObject Win32_NTLogEvent
    = Properties: Category; CategoryString; EventCode; EventIdentifier; TypeEvent; InsertionStrings; LogFile; Message; RecordNumber; SourceName; TimeGenerated; TimeWritten; Type; UserName
    + complette Message
    – TimeGenerated – special WMI-DateTime format
    – not easy to take a List of all EventLogs
    – be aware of using RAM by WMI
    – the slowest

    * Get-WinEvent
    = Properties: TimeCreated; ProviderName; Id; Message
    – incomplette Message : The description for Event ID ” in Source ” cannot be found…..
    – no List of all EventLogs
    – very simple filters and HashFilter not working in Win2008

    * Get-EventLog
    = Properties: Index; EntryType; InstanceId; Message; Category; CategoryNumber; ReplacementStrings; Source; TimeGenerated; TimeWritten; UserName
    – incomplette Message : The description for Event ID ” in Source ” cannot be found…..
    + After & Before for DateTime filter
    + List of ALL EventLogs including “Applications adn Services logs” [Get-EventLog -list]

    … so.. script to collect only new Events not yet in DB:

    ForEach ($EventLog in $(Get-EventLog -list)) {
      ForEach ($Event in Get-EventLog -LogName $EventLogName -After $LastEventDateInDB -Before $ScriptStarted){
        if ($EventMessage -match 'The description for Event ID .* in Source .* cannot be found'){
          $WMIWin32NTLogEvent = Get-WmiObject Win32_NTLogEvent -Filter "Logfile='$EventLogName' AND RecordNumber = '$EventIndex'"
          $EventMessage = $WMIWin32NTLogEvent.Message
      }
    }

  6. Mark Berry Post author

    VasekB, when the description is not found, I pull the message from other event properties.

    For Get-WinEvent:

    # Custom handling of $_.Message
    if (    ($_.Message -eq $null) `
        -or ($_.Message -like "*Either the component that raises this event is not installed on your local computer or the installation is corrupted.*") ) {
      # ReplacementStrings are not exposed in this object, so convert to XML and get Data
      $EventXML = $_.ToXML()
      $Message = $EventXML.Event.EventData.Data
    }
    else {
      $Message = $_.Message
    }

    For Get-EventLog (maybe this one should check “-eq $null” as well):

    # Custom handling of $_.Message
    if ($_.Message -like "*The local computer may not have the necessary registry information or message DLL files*") {
      # A custom message is stored in the ReplacementStrings.  If there are multiple
      # ReplacementStings, PowerShell treats as them an array, so convert to a single string
      $Message = $_.ReplacementStrings | Out-String -Width 8192
    }
    else {
      $Message = $_.Message
    }

    Does that help?

  7. VasekB

    Mark, nice idea, I’ll change in my script .. now it’s 2 times getting Message – it’s slow.
    I have 2 scripts and I’m starting them in one moment.
    1) getting Events from last time I ran script (-After $LastEventDateInDB -Before $ScriptStarted)
    script ends after putting all events to DB
    2) waitting for new Events using
    $Scope = New-Object System.Management.ManagementScope(“\.rootcimV2”)
    $EventQuery = “TargetInstance ISA ‘Win32_NTLogEvent'” + $EventLogsFilter
    $EventQueryWQL = New-Object System.Management.WQLEventQuery (“__InstanceCreationEvent”,$TimeSpan, $EventQuery )

  8. Sanchit

    I wanted to fetch all types of Windows Logs through PowerShell as follows-

    Application,
    Security,
    System,
    Setup,
    Forwarded Events

    I can get Application and System logs through Get-EventLog command but I am not able to get the Security, Setup, Forwarded Events through it.

    I tried using Get-WinEvent -LogName. It worked for Setup and Security but not for Forwarded events. Error which I get is-

    Get-WinEvent : No events were found that match the specified selection criteria.

    Question #1. Is it due to no log(s) entries in Forwarded Events? If yes, how can I create an entry in Forwarded Events & verify if that is the case.

    Also, response of Get-EventLog command includes following heads-

    Index
    Time
    EntryType
    Source
    InstanceID
    Message

    but, response of Get-WinEvent -LogName command does not includes full of them. It just includes-

    TimeCreated
    Id
    LevelDisplayName
    Message

    Question #2. Is there any way by which I can responses same as Get-EventLog command?

    Kindly help.

    Thanks

  9. Mark Berry Post author

    Sanchit, I have never used ForwardedEvents; in fact I see on my system that it is Disabled. Is this the log that is used when receiving events forwarded from other systems? Maybe you could get something into the log that way.

    Get-WinEvent -LogName returns objects. Each object contains the properties specified in System.Diagnostics.Eventing.Reader.EventLogRecord (see link in main article above). Some names have changed, e.g. Source is now ProviderName. You can create a custom list of columns in a format-table command, for example:

    get-winevent -logname Application | format-table -property TimeCreated,ProviderName,Message

  10. mbourgon

    Hey, Mark. Wanted to give you a big thanks for this. I’ve been building my own log puller, saving App/System (not security) to database, and only grabbing newer records. Based off this I went down the FilterXML rabbit hole, only to find out it took substantially longer than simple gwmi Win32_NTlogEvent with “-query”, (which filters based on timewritten, logfile and recordnumber). Now to see how filterhashtable compares on 2008 R2…

    Adding the code here for posterity, in case anybody else needs it, and because figuring this out took me far too long. (but you’re still better off using gwmi, at least for WS2008)
    (Get-WinEvent -ComputerName $computername -LogName "Application" -FilterXPath "*[System[TimeCreated[@SystemTime>='2015-04-29T01:19:25.000Z'] and (EventRecordID>=13736099)]]")|select *
    #in testing, gwmi took 9 seconds, and get-winevent took 30.

  11. Mark Berry Post author

    Thanks mbourgon. I get the impression that Get-WinEvent is better if you have more criteria and extract a smaller subset, e.g. if you are searching for specific sources or searching only for Warnings and Errors. For log dumping of entire System and Application logs, maybe there is an export / import flow that would be better. I also still use the very powerful Log Parser 2.2 but that’s a whole new (well, old) way to pull logs with its own syntax and quirks so be warned: https://www.microsoft.com/en-us/download/details.aspx?id=24659.

  12. mbourgon

    Howdy, again! Done; wound up building it off of GWMI. Testing showed it was TONS faster to do it via GWMI than either get-eventlog or get-winevent. My old version used LogParser, and worked for 5 years, but took something like 4 hours to get things. The new one runs every 15 minutes and takes 2 minutes to collect everything. The next step for it would be remoting, but I’ve got a bunch of ancient servers so can’t always do that. Next version! : )

  13. Mark Berry Post author

    mbourgon, thanks for the follow-up! Nice to know of the GWMI option. Does that work with newer logs as well (Windows Backup etc?) or is it limited to Application and System?

  14. Raymond Coates

    Nice work on the comparison and lots of good info gathered as a result. With respect to the description not being found when querying an event log I found that localization plays into this. Before querying for the event log entry I store the current culture, set the culture to en-US, perform the query, then set the culture back to the saved culture.

  15. Pingback: Reading the Event Log with Windows PowerShell

Leave a Reply

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

Notify me of followup comments via e-mail. You can also subscribe without commenting.