PyWin32: Getting Windows Event Logs

The other day, there was a post on one of the mailing lists that I follow about accessing the Windows Event Logs. I thought that was an interesting topic, so I went looking for examples and found a pretty nice example on ActiveState. In this article, you’ll find out what I discovered.

It’s probably easiest to just jump right into the code. Note that the only thing other than Python that you will need is the PyWin32 package. Once you’ve got that, then you can follow along:

 
import codecs
import os
import sys
import time
import traceback
import win32con
import win32evtlog
import win32evtlogutil
import winerror

#----------------------------------------------------------------------
def getAllEvents(server, logtypes, basePath):
    """
    """
    if not server:
        serverName = "localhost"
    else: 
        serverName = server
    for logtype in logtypes:
        path = os.path.join(basePath, "%s_%s_log.log" % (serverName, logtype))
        getEventLogs(server, logtype, path)

#----------------------------------------------------------------------
def getEventLogs(server, logtype, logPath):
    """
    Get the event logs from the specified machine according to the
    logtype (Example: Application) and save it to the appropriately
    named log file
    """
    print "Logging %s events" % logtype
    log = codecs.open(logPath, encoding='utf-8', mode='w')
    line_break = '-' * 80
    
    log.write("\n%s Log of %s Events\n" % (server, logtype))
    log.write("Created: %s\n\n" % time.ctime())
    log.write("\n" + line_break + "\n")
    hand = win32evtlog.OpenEventLog(server,logtype)
    total = win32evtlog.GetNumberOfEventLogRecords(hand)
    print "Total events in %s = %s" % (logtype, total)
    flags = win32evtlog.EVENTLOG_BACKWARDS_READ|win32evtlog.EVENTLOG_SEQUENTIAL_READ
    events = win32evtlog.ReadEventLog(hand,flags,0)
    evt_dict={win32con.EVENTLOG_AUDIT_FAILURE:'EVENTLOG_AUDIT_FAILURE',
              win32con.EVENTLOG_AUDIT_SUCCESS:'EVENTLOG_AUDIT_SUCCESS',
              win32con.EVENTLOG_INFORMATION_TYPE:'EVENTLOG_INFORMATION_TYPE',
              win32con.EVENTLOG_WARNING_TYPE:'EVENTLOG_WARNING_TYPE',
              win32con.EVENTLOG_ERROR_TYPE:'EVENTLOG_ERROR_TYPE'}
    
    try:
        events=1
        while events:
            events=win32evtlog.ReadEventLog(hand,flags,0)
        
            for ev_obj in events:
                the_time = ev_obj.TimeGenerated.Format() #'12/23/99 15:54:09'
                evt_id = str(winerror.HRESULT_CODE(ev_obj.EventID))
                computer = str(ev_obj.ComputerName)
                cat = ev_obj.EventCategory
        ##        seconds=date2sec(the_time)
                record = ev_obj.RecordNumber
                msg = win32evtlogutil.SafeFormatMessage(ev_obj, logtype)
                
                source = str(ev_obj.SourceName)
                if not ev_obj.EventType in evt_dict.keys():
                    evt_type = "unknown"
                else:
                    evt_type = str(evt_dict[ev_obj.EventType])
                log.write("Event Date/Time: %s\n" % the_time)
                log.write("Event ID / Type: %s / %s\n" % (evt_id, evt_type))
                log.write("Record #%s\n" % record)
                log.write("Source: %s\n\n" % source)
                log.write(msg)
                log.write("\n\n")
                log.write(line_break)
                log.write("\n\n")
    except:
        print traceback.print_exc(sys.exc_info())
                
    print "Log creation finished. Location of log is %s" % logPath


if __name__ == "__main__":
    server = None  # None = local machine
    logTypes = ["System", "Application", "Security"]
    getAllEvents(server, logTypes, "C:\downloads")

There are a couple potential caveats to this type of scripting. I tested this code as an Administrator on my PCs and as a Domain Administrator at work. I did not test it as any other type of user. So if you have problems getting this code to run, check your permissions. I tested this from Windows XP and Windows 7. The UAC doesn’t appear to block this activity on Windows 7, so that made it just as easy to use as XP did. However, Windows 7’s events had some unicode in the message portion of the code whereas XP did not. Watch out for that and handle it accordingly.

Anyway, let’s unpack this script and see how it works. First we have a number of imports. We use the codecs modules to encode the log file in utf-8 just in case there’s some sneaky unicode in the message. We use PyWin32’s win32evtlog module to open the event log and pull information out of it. According to the article I mentioned at the beginning, to get all the events from the log, you need to call win32evtlog.ReadEventLog repeatedly until it stops returning events. Thus, we use while loop. Inside the while loop, we use a for loop to iterate over the events and extract the event ID, record number, event message, event source and a few other tidbits. We log it and then we exit the for loop and the while loop calls the win32evtlog.ReadEventLog again.

We use the traceback module to print out any errors that occur during the script’s run. And that’s all there is to it!

Wrapping Up

As you can see, using the PyWin32 package is easy. If you get stuck, it has some great documentation. If that documentation isn’t good enough though, you can fall back on MSDN instead. PyWin32 is a light wrapper around Windows’ API, so using MSDN’s instructions is fairly simple. Anyway, I hope you learned a lot and will find it helpful.

Further Reading