Hey, Scripting Guy! Question

Hey, Scripting Guy! How can I monitor event log messages for a specific word or phrase?

-- GH

SpacerHey, Scripting Guy! AnswerScript Center

Hey, GH. You know, now that the 2007 Scripting Games are over (have we mentioned that the 2008 Games are just a year away?) it’s time for the Scripting Guys to rejoin the real world. Before we answer your question, however, could you give us a second to thumb through this pile of newspapers and see if we missed anything in the last month or so? Let’s see: boring; boring; no one cares; boring.

Oh, wait, here’s something: allegedly a NASA astronaut went off the deep end, as they say, and set out to kidnap a romantic rival. Which leads to an obvious question: have the Scripting Guys ever gone off the deep end and tried to kidnap someone?

Well, we’re ashamed to admit it, but yes, we did. Shortly after Peter Costantini accepted his new position as a Microsoft Program Manager the remaining Scripting Guys went a tad bit crazy; in fact, we kidnapped Peter, tied him up in the basement of Scripting Guys Headquarters, and delivered an ultimatum: either rejoin the team, or bake us a batch of cupcakes. (The cupcakes were Dean’s idea.) We gave him 24 hours to make up his mind.

So, yes, we did go off the deep end – once. But we’re back to normal now and, to prove it, we’ll write you a script that monitors for specific words in an event log message. (Interestingly enough, that’s the very same task Washington State Patrol officers use to test someone for drunk driving!)

For example, suppose you have a script named Test.vbs. You’d like to be notified any time an event written to the event log includes the term Test.vbs. How can you do that? Here’s how:

strComputer = "."

Set objWMIService = GetObject("winmgmts:{(Security)}\\" & _
        strComputer & "\root\cimv2")

Set colEvents = objWMIService.ExecNotificationQuery _    
    ("Select * From __InstanceCreationEvent Where " _
        & "TargetInstance isa 'Win32_NTLogEvent'")

Do
    Set objEvent = colEvents.NextEvent
    If InStr(LCase(objEvent.TargetInstance.Message), "test.vbs") Then
        Wscript.Echo Now
        Wscript.Echo "Category: " & objEvent.TargetInstance.Category
        Wscript.Echo "Event Code: " & objEvent.TargetInstance.EventCode
        Wscript.Echo "Message: " & objEvent.TargetInstance.Message
        Wscript.Echo "Record Number: " & objEvent.TargetInstance.RecordNumber
        Wscript.Echo "Source Name: " & objEvent.TargetInstance.SourceName
        Wscript.Echo "Event Type: " & objEvent.TargetInstance.Type
        Wscript.Echo
    End If
Loop

As you can see, we start out by connecting to the WMI service on the local computer (although this script can also monitor event logs on remote machines). Notice, however, that our WMI binding string includes the Security privilege: Set objWMIService = GetObject("winmgmts:{(Security)}\\". Is that important? You bet it is, provided that you want to monitor all the event logs, including the Security event log. If that’s the case then you have to include the Security privilege. If you’re not interested in the Security event log, however, then the answer is no, this isn’t important at all.

After connecting to the WMI service we issue the following query, a query that asks WMI to notify us any time a new event gets written to any of our event logs:

Set colEvents = objWMIService.ExecNotificationQuery _    
    ("Select * From __InstanceCreationEvent Where " _
        & "TargetInstance ISA 'Win32_NTLogEvent'")

What we’re doing here is monitoring for new object instances (__InstanceCreationEvent), but only if those new objects happen to be members of the Win32_NTLogEvent class. You might have noticed that we don’t include a “polling interval” (e.g., WITHIN 10) in this query. Why not? Because polling intervals are used only when monitoring WMI classes that don’t have a custom event provider. That’s not the case with the event logs; the event logs do have a custom event provider. Because of that we can get instant notification of any new events, without having to include code that instructs WMI to periodically go out and check for new events.

Note. Of course that doesn’t make any sense; didn’t you realize this is the Hey, Scripting Guy! column?!? But seriously, folks, if you could use a little more information about WMI monitoring scripts – as well as a definition of terms like polling interval – then you might want to view the Scripting Guys webcast An Ounce of Prevention: An Introduction to WMI Events.

After issuing our query we set up a Do loop that’s designed to run forever and ever. That, by the way, is one reason why you should run this script in a command window under the CScript script host; if you ever decide that you do want to terminate the script you can do so simply by closing the command window in which the script is running.

Inside that loop we use this block of code to instruct the script to sit around and wait until an event gets written to one of the event logs:

Set objEvent = colEvents.NextEvent

And what happens when an event does get written to one of the event logs? Well, when that happens we use the following line of code to see if the event message includes the term test.vbs:

If InStr(LCase(objEvent.TargetInstance.Message), "test.vbs") Then

What are we doing here? Well, first of all we’re grabbing the Message property from the TargetInstance object, an object that represents the record that was just written to the event log. We use the LCase function to convert all the characters in that message to lowercase, then use the InStr function to determine whether the value test.vbs can be found anywhere in that message. If test.vbs does not appear anywhere in the message then we simply loop around and wait for the next event to occur.

But let’s assume that the event message does include the text test.vbs. In that case we simply use this block of code to echo back information about that event:

Wscript.Echo Now
Wscript.Echo "Category: " & objEvent.TargetInstance.Category
Wscript.Echo "Event Code: " & objEvent.TargetInstance.EventCode
Wscript.Echo "Message: " & objEvent.TargetInstance.Message
Wscript.Echo "Record Number: " & objEvent.TargetInstance.RecordNumber
Wscript.Echo "Source Name: " & objEvent.TargetInstance.SourceName
Wscript.Echo "Event Type: " & objEvent.TargetInstance.Type

Needless to say, this is pretty standard WMI scripting: we simply echo back the current date and time (using the Now function), then echo back property values such as Category, EventCode, and Message. If you aren’t sure what those property values are used for, well, too bad for you, huh?

No, hey, just kidding. What we meant to say was this: if you’d like more information about event logs and the Win32_NTLogEvent class then you might want to take a look at this section of the Microsoft Windows 2000 Scripting Guide.

Oh: and if you’d like to test today’s script here’s some code that will write an event to the Application event log, an event that – fortuitously enough, includes the term test.vbs:

Const EVENT_SUCCESS = 0

Set objShell = Wscript.CreateObject("Wscript.Shell")
objShell.LogEvent EVENT_SUCCESS, "Test.vbs started."

So what did happen with Peter? Believe it or not, we turned him loose after about 15 minutes; it only took that long for us to remember why we let him go in the first place. To add insult to injury, though, not only did Peter win his freedom, but he somehow also talked the rest of us into baking him cupcakes. But, then again, that’s probably how Peter came to be such a powerful and important Program Manager while the rest of us are, well, Scripting Guys.