September 7th, 2007

How Can I Backup an Event Log to a Text File?

Hey, Scripting Guy! Question

Hey, Scripting Guy! How can I backup an event log to a text file?

— IMBDS

SpacerHey, Scripting Guy! AnswerScript Center

Hey, IMBDS. You know, the Scripting Guys have dedicated their lives to helping others. (Well, OK, originally we dedicated our lives to having others help us. But when no one seemed very interested in that we changed our minds.) That means that, on those rare occasions when we actually learn something, we make a point of keeping the rest of the world informed as well.

For example, just yesterday we were learned (much to our surprise) that the Do Not Call registry in the US (designed to prevent telemarketers from barraging you with phone calls) has an expiration date: if you register your phone number, that registration is only good for 5 years. (Because, apparently, most people can only go so long before they absolutely have to hear from a telemarketer.) For example, the Scripting Guy who writes this column just entered his home phone number into the registry’s online verification tool, and discovered that his registration is due to expire in a year.

Wonderful.

Note. If you want to know the truth, we’re not sure that the Do Not Call registry is all that effective anyway. For example, even though his phone number is registered, the Scripting Guy who writes this column gets several phone calls a day asking him for money.

And those are just from the Scripting Son.

Anyway, we thought everyone should know that the Do Not Call registry is anything but permanent. We also thought people should know that there’s no simple, straightforward way to backup an event log to a text file; as it is, WMI’s BackupEventLog method can only save data using the event log binary format. Of course, that doesn’t mean that you can’t save event log data to a text file; it just means you have to do that using a script like this one instead:

strComputer = “.”

Set objWMIService = GetObject(“winmgmts:\\” & strComputer & “\root\cimv2”)

Set colEvents = objWMIService.ExecQuery _ (“Select * from Win32_NTLogEvent Where LogFile=’Application'”)

Set objFSO = CreateObject(“Scripting.FileSystemObject”) Set objFile = objFSO.CreateTextFile(“C:\Scripts\Events.txt”)

For Each objEvent in colEvents strTimeWritten = objEvent.TimeWritten

dtmTimeWritten = CDate(Mid(strTimeWritten, 5, 2) & “/” & _ Mid(strTimeWritten, 7, 2) & “/” & Left(strTimeWritten, 4) _ & ” ” & Mid (strTimeWritten, 9, 2) & “:” & _ Mid(strTimeWritten, 11, 2) & “:” & Mid(strTimeWritten, 13, 2))

dtmDate = FormatDateTime(dtmTimeWritten, vbShortDate) dtmTime = FormatDateTime(dtmTimeWritten, vbLongTime)

strEvent = dtmDate & vbTab strEvent = strEvent & dtmTime & vbTab strEvent = strEvent & objEvent.SourceName & vbTab strEvent = strEvent & objEvent.Type & vbTab strEvent = strEvent & objEvent.Category & vbTab strEvent = strEvent & objEvent.EventCode & vbTab strEvent = strEvent & objEvent.User & vbTab strEvent = strEvent & objEvent.ComputerName & vbTab

strDescription = objEvent.Message If IsNull(strDescription) Then strDescription = “The event description cannot be found.” End If strDescription = Replace(strDescription, vbCrLf, ” “) strEvent = strEvent & strDescription

objFile.WriteLine strEvent Next

objFile.Close

Admittedly, this is a somewhat complicated-looking script. And there’s a good reason for that: some of the things we need to do here are somewhat complicated. But don’t fret; we’re going to walk you through the entire thing and explain exactly how it all works.

Just as soon as we answer this phone call from an Unknown Caller.

The script starts out simply enough, connecting to the WMI service on the local computer. And, being the Microsoft Scripting Guys, we know what you’re thinking. And the answer to your question is yes, you can run this script against a remote computer; all you have to do is assign the name of that remote computer to the variable strComputer, like so:

strComputer = “atl-dc-01”

Ah, that’s no big deal. When you’re a Scripting Guy you’re supposed to know about things like that.

After making the connection to the WMI service we then execute the following line of code, a line of code that returns a collection of all the events found in the Application event log:

Set colEvents = objWMIService.ExecQuery _
    (“Select * from Win32_NTLogEvent Where LogFile=’Application'”)

Note. Needless to say, you aren’t limited to backing up just the Application event log; if you want to, you can back up any (or even all) your event logs. For more information (particularly information about working with the Security event log) see the Microsoft Windows 2000 Scripting Guide.

That brings us to these two lines of code:

Set objFSO = CreateObject(“Scripting.FileSystemObject”)
Set objFile = objFSO.CreateTextFile(“C:\Scripts\Events.txt”)

As we noted earlier, there’s no built-in method for backing up an event log as a text file; that is, there’s no WMI method like, say, BackupAsTextFile. That means that there’s only one way for us to programmatically save the contents of an event log as a text file: we need to write the information to the text file ourselves. So how do we do that? Well, for starters, we use the preceding two lines of code to create an instance of the Scripting.FileSystemObject object, and then create a brand-new text file named C:\Scripts\Events.txt.

And then it’s time to do some real work. We should probably note that it actually is possible to save an event log as a text file … provided that you open up Event Viewer and do all the work yourself. If you do that, Event Viewer will save the event log as a tab-delimited file. We’ve decided to use this same format: we’re going to save all the events in the Application event log in tab-delimited format. (That is, we’ll separate the individual fields for each event – things like the event code and the event description – using tabs.)

To that end, the first thing we do is create a For Each loop that loops us through each of the events in our collection. Inside the loop, we grab the value of the TimeWritten property and stash it in a variable named strTimeWritten:

strTimeWritten = objEvent.TimeWritten

Here’s one place where things get a tad bit complicated. As you probably know, WMI stores dates and times using the UTC (Universal Time Coordinate) format; that means the value of the TimeWritten property is going be something like this:

20070905121045.578000-420

Nice, huh? We won’t bother explaining how to decipher a UTC date-time value; you can find a pretty good explanation of that in the Scripting Guide. Suffice to say, however, that a value like 20070905121045.578000-420 isn’t quite as intuitive as information like this:

9/5/2007 12:10 PM

But how in the world do we convert a UTC value to something a human being can read and understand? Why, like this, of course:

dtmTimeWritten = CDate(Mid(strTimeWritten, 5, 2) & “/” & _
    Mid(strTimeWritten, 7, 2) & “/” & Left(strTimeWritten, 4) _
        & ” ” & Mid (strTimeWritten, 9, 2) & “:” & _
            Mid(strTimeWritten, 11, 2) & “:” & Mid(strTimeWritten, 13, 2))

Again, you can find a complete description of what we’re doing in the Scripting Guide. As a quick summary, all we’re really doing here is grabbing the individual pieces of the UTC value and constructing our own date-time value. For example, the first four digits in a UTC value represent the current year; thus 20070905121045.578000-420 indicates that we’re dealing with the year 2007. In turn, that means code like this, which snags the first four characters in our UTC value, also lets us know that we’re dealing with the year 2007:

Left(strTimeWritten, 4)

If you follow the logic of the script all the way through, you’ll see that, eventually, the variable dtmTimeWritten gets assigned a value like this:

9/5/2007 12:10:45 PM

Now that’s something we can work with.

As it turns out, the tab-delimited file format used by Event Viewer separates the date (9/5/2007) from the time (12:10:45 PM). That’s fine; because of that, we next use the FormatDateTime function and the VBScript constants vbShortDate and vbLongTime to store the date in a variable named dtmDate and the time in a variable named dtmTime:

dtmDate = FormatDateTime(dtmTimeWritten, vbShortDate)
dtmTime = FormatDateTime(dtmTimeWritten, vbLongTime)

Once we’ve taken care of the date and time, our next step is to begin constructing the first line of the text file (which will correspond to the information in the first event recorded in the event log). That’s what this block of code is for:

strEvent = dtmDate & vbTab
strEvent = strEvent & dtmTime & vbTab
strEvent = strEvent & objEvent.SourceName & vbTab
strEvent = strEvent & objEvent.Type & vbTab
strEvent = strEvent & objEvent.Category & vbTab
strEvent = strEvent & objEvent.EventCode & vbTab
strEvent = strEvent & objEvent.User & vbTab
strEvent = strEvent & objEvent.ComputerName & vbTab

As you can see, there’s nothing too fancy here. In the first line, we assign the date (represented by the variable dtmDate) and a tab character (the VBScript constant vbTab) to a variable named strEvent. (Why a tab character? Because we want each field to be separated by a tab.) In line 2, we assign strEvent the current value of strEvent plus the value of the variable dtmTime plus another tab character. We then repeat this process several times, tacking the values of WMI properties such as SourceName and Type to the value of strEvent.

That brings us to this block of code:

strDescription = objEvent.Message
If IsNull(strDescription) Then
    strDescription = “The event description cannot be found.”
End If
strDescription = Replace(strDescription, vbCrLf, ” “)
strEvent = strEvent & strDescription

Perhaps the most important property of an event log event is the Message property; the Message property, as you doubtless know, provides a (more or less) detailed description of the event. Unfortunately, however, the Message property can potentially cause havoc with our script. So what do we do about that? That’s easy: we take steps to defuse these problems before they occur.

What kind of problems are we talking about? Well, for one thing, the Message property often includes one or more carriage return-linefeed characters; in other words, the value of the Message property might look like this:

Security policy in the Group policy objects has been applied successfully.

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

What’s wrong with that? Well, in a tab-delimited file, all the information for a single event must fit on one line; that’s not going to be the case if the Message includes a carriage return-linefeed character. Consequently, we use the Replace function to replace any carriage return-linefeed characters (vbCrLf) with a blank space:

strDescription = Replace(strDescription, vbCrLf, ” “)

That’s great, but, in turn, this introduces another problem. For some events the Message property doesn’t actually have a value. In Event Viewer, that’s no problem; in a case like that, Event Viewer displays a message similar to this:

The description for Event ID ( 0 ) in Source ( iPod Service ) cannot be found. The local computer may not have the
necessary registry information or message DLL files to display messages from a remote computer. You may be 
able to use the /AUXSOURCE= flag to retrieve this description; see Help and Support for details. The following 
information is part of the event: Service started.

But that works only in Event Viewer; that information does not get passed to the Message property. Instead, the value of the Message property comes back as Null.

Is that a problem? As a matter of fact, it is; running the Replace function against a Null value is a recipe for disaster. (Assuming, of course, that you consider a script that crashes to be a disaster.) Therefore, before we call the Replace function we execute this block of code:

If IsNull(strDescription) Then
    strDescription = “The event description cannot be found.”
End If

All we’re doing here is using the IsNull function to determine whether the Message property (which is stored in the variable strDescription) happens to be Null. If it’s not, well, no problem. If it is, then we simply set the value of strDescription to the generic message “The event description cannot be found.”

After dealing with any potential Null values, we call the Replace function, then tack the value of strDescription onto the end of our event string:

strEvent = strEvent & strDescription

And then we simply call the WriteLine method to write this event to the text file:

objFile.WriteLine strEvent

From there we zip back to the top of our For Each loop and repeat the process with the next event in the collection. After we’ve added all the events to the text file we execute the Close method, close the file, and call it a day.

We hope that answers your question, IMBDS. If it doesn’t, don’t bother to send us an email; instead, just give us a call. After all – sigh – that’s what everyone else does. Oops; gotta go. The phone’s ringing.

Note. In the true spirit of entrepreneurship, there are companies that will actually call you, on the phone, and (for a small fee) offer to put your phone number on the Do Not Call registry. Only in America ….

Author

0 comments

Discussion are closed.