\[Editor's Note: Scripting Solutions is about using scripts to build solutions to specific business problems. This column doesn't teach you how to program with scripting languages, but how to use scripts to provide quick, easy-to-implement tools you can use right away.\]
The event logs are Windows NT's central repositories for software- and system-related status information. Events are status messages that drivers, services, or applications generate. Driver- and service-related events generally write to the System Log, and Win32 applications write to the Application and Security logs. NT's event architecture gives software developers a common event reporting mechanism. This mechanism provides a centralized logging facility that lets systems administrators perform important actions such as troubleshooting system and application problems or performing security audits. Unfortunately, based on the proprietary format of log files, administrators are limited when it comes to managing them. Perl for Win32 fixes this problem by giving the administrator a robust set of functions to easily manage NT event logs. This article is the first of a two-part series that demonstrates Perl for Win32's event log capabilities. In Part 1, I examine a simple Perl script that writes to the NT Application Log. In Part 2, I'll look at a script that searches NT event logs for specific events.
Getting to the Event
NT logs events in the Application, Security, and System event logs (appevent.evt, secevent.evt, and sysevent.evt files located in the %SystemRoot%\system32\config directory). The Event Viewer (eventvwr.exe) is the primary user interface into the three log files. Although the Event Viewer lets you view a local or remote machine's event logs, it doesn't let you perform some other useful functions.
For example, have you ever wanted to print an event or filter a group of events and save or print the filtered results? What about searching for an event across multiple systems to determine the magnitude of a Windows Internet Naming Service (WINS), Exchange, or other distributed application problem? You might also like to have a command-language (or batch) function to send a user-defined event to one of the three NT event logs.
The Microsoft Windows NT Server Resource Kit utilities, dumpel.exe and logevent.exe, are not useful for these tasks for a couple of reasons. Dumpel.exe parses the entire target log. It doesn't let you specify how far back in time to search, making the utility very difficult to use if you want to verify the daily occurrence of an event. Logevent.exe doesn't let you specify a unique Event Source or Event ID. You're restricted to "User Event" as the Event Source and "1" as the Event ID. In this article, I'll look at a Perl script that overcomes the deficiencies in the logevent.exe utility and takes advantage of Perl for Win32's EventLog module; in Part 2, I'll fix dumpel.exe. (For basic information about Perl, see "NT Administration Wizardry with Perl for Win32," January 1998.)
When You Get There
Many scenarios make generating your own events a useful function, including reporting the success or failure of a batch file, reporting the status of a scheduled or distributed task to a central console, reporting system or application availability to a central console, and reporting the amount of time a specific task takes to complete. The script in Listing 1, GenEvent.pl, demonstrates Perl's ability to write to the NT Application Log.
GenEvent.pl includes the Perl for Win32 EventLog module. The EventLog module provides the methods in Table 1 for interacting with NT event logs. You can find additional instructions for using these functions in the Perl for Win32 documentation (see win32mod.html). I also recommend a visit to Philippe Le Berre's Perl documentation (http://www.inforoute.cgs.fr/leberre1/main.htm) for a more thorough discussion of the EventLog module.
The second line in GenEvent.pl, Listing 1, checks whether the user entered a question mark as the first command-line argument. If so, the script calls the PrintHelp subroutine, which appears in the lower half of Listing 1. PrintHelp writes how-to information to standard output (STDOUT) and then exits. PrintHelp uses the same "here document" approach as the script in the January article.
If the user didn't enter a question mark as the first command line argument, the lines at callout A in Listing 1 analyze each element in the command-line argument array, @ARGV. Through each iteration, the foreach loop assigns an element of @ARGV to Perl's default input variable $_ and compares $_ to regular expressions that match the command-line switch options (h=, s=, i=, t=, and d=). If the program finds a regular expression match, it assigns the option's value (the part following the equal sign) to a corresponding scalar variable.
Perl's split function separates the command-line switch from its value. Split chops a string into an array of strings. Here, the function splits the contents of $_ into two parts based on the "=" pattern. The \[1\] subscript takes the second element of the array that split returns and assigns it to the appropriate scalar variable.
After completing the foreach loop, the script assigns a default value to any options that weren't defined on the command line. Next, the code checks the user-specified or default event type (Error, Information, or Warning) and assigns the corresponding constant to the $type scalar as defined in EventLog.pm. At B, the code initializes an event record hash with the scalar values $id, $type, and $desc.
Notice the use of the and and or operators in the code so far. In Perl, we call these operations short-circuit evaluations. They're simply a more natural way to say if...then. When I say (a and b), b is evaluated only if a is true. Likewise, if I say (a or b), b is evaluated only if a is false. At C, you see that Perl also provides && (and) and || (or) versions of these operators. The difference between the two versions is precedence; the text versions (and, or) have lower precedence and are generally preferable.
At C, the script opens and writes to the NT Application Log. The Win32::EventLog::Open($EventObj, $src, $host) function opens the Application Log and returns a handle to the opened log in the $EventObj scalar. The code uses the returned handle in the $EventObj>Report(\%Event) call, which writes the event to the log. Notice that I pass a reference for the hash I initialized at B to the Report method.
Screen 1, page 193, shows GenEvent.pl in action. I execute the script via the command line by passing the script to the Perl interpreter. In Screen 1, I execute the script three consecutive times, altering the Event ID, Event Type, and Description with each invocation. Screen 2, page 193, shows the result in the target host's Application Log. (For an easy method of running Perl scripts on a Windows NT or Windows 95 machine, see the sidebar, "Executable Perl Scripts.")
What You Take With You
When you combine GenEvent.pl with one of the many third-party event log monitoring tools, you create a way to track the results of all those automation scripts you've been writing in Perl. In the event (no pun intended) you don't have an event log monitor, tune-in next month for Part 2, in which I'll continue to examine the EventLog module by writing an enterprise-capable Event Log chainsaw (or parser).