Parsing Event Logs using System.Diagnostics.Eventing.Reader

I’ve just had to analyse a bunch of Event Logs that contain exceptions, produced from a load testing exercise. I needed to turn them into a summary of the counts of each class of exception that occurred.

The exceptions belonging to each class of exception message didn’t generate exactly the same text in the event log data every time. So I decided the simplest way to categorise them was to have each category able to work out if it matches event log data using one of two criteria: either data contains a substring; or data matches a Regex. Flexible enough for the fairly simple requirements of this scenario.

After a little research, I found out about the existence of the System.Diagnostics.Eventing.Reader class, which will parse event logs from either the local or a remote computer, or from an EVTX file. The event logs I was parsing already needed to be saved into a file and archived after each run, so I made the parser use the archived files. I’m still keen to play with parsing logs directly off remote computers at some point.

Here’s some code. I haven’t included all the boring declaration of constants and the like in these code snippets, just the interesting bits. Some of it’s a wee bit hack-ish and not properly structured or parameterised; as this is a small utility for occasional use, the effort to make it really nice isn’t currently justified.

The EventLogMatcher class itself:

public class EventLogMatcher
{
  public EventLogMatcher(string dataContains, string dataRegex, string description)
  {
    if (!(string.IsNullOrEmpty(dataContains) ^ 
        string.IsNullOrEmpty(dataRegex)))
      throw new ArgumentException
        ("One and only one of dataContains and dataRegex must be specified");
    DataContains = string.IsNullOrEmpty(dataContains) 
      ? null
      : dataContains;
    DataRegex = string.IsNullOrEmpty(dataRegex) 
      ? null 
      : new Regex(dataRegex, RegexOptions.Singleline);
    Description = description;
  }

  public string DataContains { get; private set; }

  public Regex DataRegex { get; private set; }

  public string Description { get; private set; }

  public bool IsDataMatch(string data)
  {
    return (DataContains != null && data.Contains(DataContains)) || 
      (DataRegex != null && DataRegex.IsMatch(data));
  }
}

The main loop of the program itself – it processes all the EVTX files in the SourcePath directory:

foreach (string sourceFile in Directory.EnumerateFiles(SourcePath, SourcePattern))
{
  string outputFile = DeriveOutputFilename(sourceFile);
  IDictionary<EventLogMatcher, int>; logMatchCounts = GetInitialisedLogTypeCounts();
  List<UnmatchedEventLog> unmatchedLogs = new List<UnmatchedEventLog>();

  EventLogReader logReader = new EventLogReader(new EventLogQuery(sourceFile, PathType.FilePath));
  for (EventRecord eventInstance = logReader.ReadEvent(); eventInstance != null; 
    eventInstance = logReader.ReadEvent())
  {
    EventLogMatcher matcher = logMatchCounts.Keys.
	  SingleOrDefault(key => key.IsDataMatch(GetData(eventInstance)));
    if (matcher == null)
      unmatchedLogs.Add(ToUnmatchedLog(eventInstance));
    else
      logMatchCounts[matcher]++;
  }
  WriteMatchedResults(outputFile, logMatchCounts, unmatchedLogs);
};

I love using System.Xml.Linq. It makes parsing XML files really simple, and quite readable. Especially for a utility like this where proper error handling isn’t important.

The code for getting the dictionary of EventLogMatchers and counts:

private static IDictionary<EventLogMatcher, int> GetInitialisedLogTypeCounts()
  {
    return LogTypes.ToDictionary(type => type, type => 0);
  } 

  private static IEnumerable<EventLogMatcher> GetParsedLogTypes()
  {
    XDocument source = XDocument.Load(KnownEventLogTypesFileName);
    return source.Elements(EventLogsTypesXName).
      Elements(EventLogsTypeXName).
      Select(ParseAsLogMatcher);
  }

  private static EventLogMatcher ParseAsLogMatcher(XElement element)
  {
    string dataContains = element.GetValueOfOptionalAttribute(DataContainsXName);
    string dataRegex = element.GetValueOfOptionalAttribute(DataRegexXName);
    string description = element.Attribute(DescriptionXName).Value; 
    return new EventLogMatcher(dataContains, dataRegex, description, ignored, defaultReason);
  }

internal static class XElementExtensions
{
  public static string GetValueOfOptionalAttribute(this XElement element, XName attributeName)
  {
    XAttribute attribute = element.Attribute(attributeName);
    return attribute == null ? null : attribute.Value;
  }
}

The actual XML for KnownEventLogTypes.xml is structured like this

<?xml version="1.0" encoding="utf-8" ?>
<EventLogsTypes>
  <EventLogsType 
     dataContains="System.Net.WebException: The operation has timed out" 
     description="The operation has timed out" />
  <EventLogsType
     dataRegex="Internal failure Sorry, there was an error. Please try again later.*AnAgent.GetSomethingInteresting"
     description="Internal failure in AnAgent. GetSomethingInteresting" />
</EventLogsTypes>

Not rocket science, but simple and effective.

Next time around I’d look at Powershell to do this: Get-EventLog also looks like a simple way to deal with event logs. But I’m glad to have had the opportunity to learn about System.Diagnostics.Eventing.Reader.

One thought on “Parsing Event Logs using System.Diagnostics.Eventing.Reader”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s