Search code examples
c#event-logsystem.diagnostics

How to create an EventBookmark when querying the event log


I have an EventLogReader object, and a query in the event log that looks like this:

string query = "*[System[(Level=2) and TimeCreated[@SystemTime>='%LastRun%']]]")

The code basically uses the reader to query for all the events that match the search query since the last time the reader was run.

I would rather use the EventBookmark for this purpose. That's what it is for, after all! But I am having trouble finding any working code.

My existing code run, in part, like this:

// This line replaces the %LastRun% code with the date
var myQuery = myEventLogQuery.Query.Replace("%LastRun%", myEventLogQuery.LastRun.ToString("o"));
var query = new EventLogQuery(myEventLogQuery.Log, myEventLogQuery.PathType, myQuery);

// Now set the LastRun date. I want to avoid this...
myEventLogQuery.LastRun = DateTime.UtcNow;

// ... by making this next line smarter.
var reader = new EventLogReader(query);
// var reader = new EventLogReader(query, ??? new EventBookmark());

EventRecord eventRecord;
while ((eventRecord = reader.ReadEvent()) != null)
{
    EventRecords.Add(new EventRecordItem(eventRecord));
}

I should be able to use the EventBookmark property of the first (or last) EventRecord to restrict the next query, but I want to set the EventBookmark initially to be basically the highest record in the log.

When the application runs initially, I don't need it to tell me about all the event log entries from the past, I only care about ones that occur after the application starts.


Solution

  • OK, I went ahead and experimented a lot, and managed to work out a good system for this. Since this topic isn't really covered, I'm posting my answer here. I hope it's useful!

    First challenge is creating the initial EventBookmark. You can't just instantiate one. You need to derive one from an existing EventRecord. To do this, I query for the last item in the log and base my EventBookmark on that.

    using System.Diagnostics.Eventing.Reader;
    public class MyEventLogQuery
    {
        public string Log { get; set; }
        public PathType PathType { get; set; }
        public string Query { get; set; }
        public EventBookmark Bookmark { get; set; }
    
        public MyEventLogQuery(string log = "Application", PathType pathType = PathType.LogName, string query = "*[System[(Level=2)]]")
        {
            Log = log;
            PathType = pathType;
            Query = query;
            Bookmark = GetBookmark(log, pathType); // Query is not important here
        }
    
        // This method returns the bookmark of the most recent event
        // log EventRecord or null if the log is empty
        private static EventBookmark GetBookmark(string log, PathType pathType)
        {
            var elq = new EventLogQuery(log, pathType) {ReverseDirection = true};
    
            var reader = new EventLogReader(elq);
            var record = reader.ReadEvent();
            if (record != null)
                return record.Bookmark;
            return null;
        }
    }
    

    Next step is to use the bookmark (or lack of bookmark) when subsequently querying the Event Log.

    using System.Diagnostics.Eventing.Reader;
    public class EventLogTracker()
    {
        public List<MyEventLogQuery> Queries { get; set; }
    
        // ... snipped some stuff
        public int Run()
        {
            var count = 0;
            foreach (var myQuery in Queries)
            {
                var query = new EventLogQuery(myQuery.Log, myQuery.PathType, myQuery.Query);
    
                // This is the important bit. Must take account that the
                // log may have been empty, so bookmark could be null
                var reader = myQuery.Bookmark != null ? new EventLogReader(query, myQuery.Bookmark) : new EventLogReader(query);
                EventRecord eventRecord;
                while ((eventRecord = reader.ReadEvent()) != null)
                {
                    // Do stuff
                    // ...
                    // Then update the bookmark
                    myQuery.Bookmark = eventRecord.Bookmark;
                    count++;
                }
            }
            return count;
        }
    

    And voila, you have code that uses the EventBookmark to only give you events that have occurred since the application was started.