views:

584

answers:

5

What is the easiest way to check if events have been logged in the eventlog during a period of time?

I want to perform a series of automated test steps and then check if any errors were logged to the Application Event Log, ignoring a few sources that I'm not interested in. I can use System.Diagnostics.EventLog and then look at the Entries collection, but it doesn't seem very useable for this scenario. For instance Entries.Count can get smaller over time if the event log is removing old entries. I'd prefer some way to either query the log or monitor it for changes during a period of time. e.g.

DateTime start = DateTime.Now;
// do some stuff...
foreach(EventLogEntry entry in CleverSolution.EventLogEntriesSince(start, "Application"))
{ 
  // Now I can do stuff with entry, or ignore if its Source is one
  // that I don't care about.
  // ...
}
+1  A: 

The System.Diagnostics.EventLog class really is the right way to do this.

Your main objection seems to be that the log can remove old entries in some cases. But you say this is in a software testing scenario. Can't you arrange to configure your test systems such that the logs are large enough to contain all entries and the removal of old entries won't occur during your tests?

Tim Farley
Fair point, only with multiple developers i wanted to be flexible with the environment in which the tests are run.
Rory
A: 

Well the solution I've come up with does use System.Diagnostics.EventLog and simply iterating over all events to filter for the ones I want. I guess this is straightforward, I just thought there would have been a more efficient interface for this. Any suggestions or improvements very welcome!

I've created a method to return event log entries since a certain time:

/// <summary>
/// Steps through each of the entries in the specified event log and returns any that were written 
/// after the given point in time. 
/// </summary>
/// <param name="logName">The event log to inspect, eg "Application"</param>
/// <param name="writtenSince">The point in time to return entries from</param>
/// <param name="type">The type of entry to return, or null for all entry types</param>
/// <returns>A list of all entries of interest, which may be empty if there were none in the event log.</returns>
public List<EventLogEntry> GetEventLogEntriesSince(string logName, DateTime writtenSince, EventLogEntryType type)
{
    List<EventLogEntry> results = new List<EventLogEntry>();
    EventLog eventLog = new System.Diagnostics.EventLog(logName);
    foreach (EventLogEntry entry in eventLog.Entries)
    {
        if (entry.TimeWritten > writtenSince && (type==null || entry.EntryType == type))
            results.Add(entry);
    }
    return results;
}

In my test class I store a timestamp:

private DateTime whenLastEventLogEntryWritten;

and during test setup I set the timestamp to when the last event log entry was written:

EventLog eventLog = new EventLog("Application");
whenLastEventLogEntryWritten = eventLog.Entries.Count > 0 ? 
     eventLog.Entries[eventLog.Entries.Count - 1] : DateTime.Now;

At the end of my test I check that there were no event log errors:

Assert.IsEmpty(GetEventLogEntriesSince("Application",
                                       whenLastEventLogEntryWritten,  
                                       EventLogEntryType.Error), 
               "Application Event Log errors occurred during test execution.");
Rory
If you could generate the XML query based on the parameters given, it would be much more performant.
CodingTales
+2  A: 

Just to be a good Wiki citizen and strive for completion, there are other ways. I didn't suggest it earlier because it is complete overkill for something that is only going to be run in-house as part of a test suite, and you said right in the title you wanted something easy.

But if you need to see events as they occur in shipping code, read on. Believe it or not there are three different Windows APIs for this thing at this point.

NotifyChangeEventLog()

The original API for this sort of thing is called NotifyChangeEventLog() and it was supported starting in Windows 2000. Essentially you use the WIN32 event log APIs to open the event log, then you call this API with the handle you were given by the other API and an event handle. Windows will signal your event when there are new event log entries to look at.

I never used this API myself, because most of my interest was in remote event log access and this API explicitly does not support remote logs. However, the rest of the API set this belongs to does let you sequentially read remote logs if you have the right permissions.

Windows Management Instrumentation

A second way is to use the Windows Management Instrumentation API, and this does support both local and remote logs. This is a COM/DCOM based API that has existed for several years in Windows, and the .NET Framework has a nice implementation of it in the System.Management namespace. Essentially what you do is create an EventQuery that looks for the appearance of new WMI objects of type (meaning within the WMI type system) of Win32_NTLogEvent. The appearance of these will indicate new event log entries, and they will present pretty much in real time. The attributes on these objects contain all the details of the log entry. There's an article from MSDN magazine that talks about playing around with this stuff in Visual Studio.

Again, this would be total overkill for a test application, it would require far more code than your existing solution. But years ago I wrote a subsystem for a network management application that used the DCOM flavor of this API to gather the event logs off of all the servers on a network so we could alert on particular ones. It was pretty slick and darn near real time. If you implement this in C++ with DCOM, be prepared to deal with Multithreaded Apartments and a lot of hairy logic to detect if/when your connection to the remote server goes up or down.

Windows Vista Event Log

Windows Vista (and Server 2008) have a whole new API suite relating to event logging and tracing. The new event log is documented here. It looks like there is an API called EvtSubscribe that allows you to subscribe to events. I have not used this API so I can't comment on its pros and cons.

Tim Farley
+1  A: 

That having been said, here's an answer that actually should be pretty straightforward even for your test application and is .NET Framework specific.

You need to open the EventLog before you start your test, and subscribe an event handler to the EventLog.EntryWritten event. This is the way that .NET exposes the NotifyChangeEventLog() Win32 API.

Move your current logic from GetEventLogEntriesSince() into the event handler, but instead of adding the events to a list for return, store them in a list you can retrieve from somewhere at the end of the run. You can retrieve the contents of the log entry from the EntryWrittenEventArgs argument which is passed, via its Entry property.

Tim Farley
A: 

I use this Powershell to scan the eventlog for relevant entries within the last 7 days:

$d=Get-Date
$recent=[System.Management.ManagementDateTimeConverter]::ToDMTFDateTime($d.AddDays(-7))

get-wmiobject -computer HOSTNAME -class Win32_NTLogEvent `
    -filter "logfile = 'Application' and (sourcename = 'SOURCENAME' or sourcename like 'OTHERSOURCENAME%') and (type = 'error' or type = 'warning') AND (TimeGenerated >='$recent')" | 
sort-object @{ expression = {$_.TimeWritten} } -descending |
select SourceName, Message | 
format-table @{Expression = { $_.SourceName};Width = 20;Label="SourceName"}, Message

If you use C# (tagged, but not mentioned in the question), the magic lies in the get-wmiobject query.

devio