How to read Windows events from Microsoft-Windows-Ntfs/Operational

Viewed 47

I wonder if someone can help with this.

I know how to read Windows events under Application, Security, System, etc. using c# .net core 6.0. But I can't read any Windows events under "Applications and Services Logs/Microsoft/Windows/Ntfs/Operational". Here is the code I use

        var log = new EventLog("Application"); // This works fine
        foreach( var entry in log.Entries)
        {
            // Do something with the event
            System.Console.WriteLine("\tEntry: " + entry.ToString());
        }

        var log2 = new EventLog("Microsoft-Windows-Ntfs/Operational"); // This does not work
        foreach( var entry in log2.Entries)
        {
            // Do something with the event
            System.Console.WriteLine("\tEntry: " + entry.ToString());
        }

I can query the events using this powershell command: Get-WinEvent -LogName "Microsoft-Windows-Ntfs/Operational" based on this thread - https://serverfault.com/questions/1067287/get-eventlog-log-microsoft-windows-ntfs-operational-fails-with-does-not-exis, but I need a way to read them in c# code.

Thanks.

Peter

1 Answers
  • The System.Diagnostics.EventLog class dates back to .NET Framework 1.x from 2001 and is only capable of reading the old-style Windows Event Logs used by Windows 2000, Windows XP, and Windows Server 2003.

    • These logs still exist through to Windows 10 and later as the top-level "Windows Logs": Application, Security, and System event-logs.
  • Windows Vista introduced a new Event Log system that is more capable (e.g. it can directly query events using an outdated and restricted subset of XPath 1.0) and other features. * (And yet, the WinForms-based Event Viewer MMC snap-in is still horribly unusable for non-trivial tasks and it hasn't been fixed since Vista came out, le sigh)

    • These new-style Logs are listed under "Applications and Services Logs" in the Windows Event viewer.
  • To access the post-Vista Event Logs you need to use System.Diagnostics.Eventing.Reader.EventLogReader instead of System.Diagnostics.EventLog.

  • Something like this works for me in LinqPad (with .NET 6):

    • Because EventLogRecord is IDisposable I think it's more prudent to copy each EventLogRecord's scalar data values (enums, strings, etc) into a custom object and immediately dispose each EventLogRecord.
using System;
using System.Diagnostics.Eventing.Reader;

public record class EventLogRecordData(
    String   LogPath,
    DateTime Created,
    String   Level,
    String   Description
)
{
    public String DescriptionTruncated => this.Description.Length >= 50 ? ( this.Description.Substring( 0, 50 ) + "..." ) : this.Description;
}

public static IEnumerable<EventLogRecordData> GetEventLogRecords( String logPath )
{
    // First, ensure the log exists:
    try
    {
        EventLogSession.GlobalSession.GetLogInformation( logName: logPath, PathType.LogName ).Dump();
    }
    catch( EventLogNotFoundException )
    {
        yield break;
    }

    //

    using( EventLogReader rdr = new EventLogReader( path: logPath ) )
    {
        for( EventRecord? e = rdr.ReadEvent(); e != null; e = rdr.ReadEvent() )
        {
            try
            {
                EventLogRecord rec = (EventLogRecord)e;
                yield return new EventLogRecordData(
                    LogPath    : logPath,
                    Created    : rec.TimeCreated ?? default,
                    Level      : rec.LevelDisplayName,
                    Description: rec.FormatDescription()
                );
            }
            finally
            {
                e.Dispose();
            }
        }
    }
}
  • The EventLogReader.ReadEvent() method returns null after it reads past the last record, but this is not currently documented - nor is the fact the method will ever return null at all, in-fact.

Used like so:

public static void Main()
{
    foreach( EventLogRecordData e in GetEventLogRecords( "Microsoft-Windows-Ntfs/Operational" ) )
    {
        Console.WriteLine( "[{0:yyyy-MM-dd HH:mm:ss}] {1} - {2}", e.Created, e.Level, e.DescriptionTruncated );
    }
}

...gives me this output (first 2 lines shown):

[2022-04-14 10:00:42] Information - IO latency summary common data for volume...
[2022-04-14 10:00:44] Information - IO latency summary common data for volume...
Related