Downloads
16404.zip

Life in the world of Win32 administration is indeed a precarious existence. Having lived there, I can attest to how difficult it is to maintain the health of a group of machines.

Fortunately, you can use event logs to help maintain the health of your machines. The event log has been around since the beginning of Windows NT and is equivalent to the UNIX syslog. An event log is a simple database that houses information about events. Basically, an event is an announcement that a program or OS component makes to indicate that something noteworthy has occurred.

Typically, event-log entries tell you that a problem has occurred. For example, an entry might tell you that a SCSI driver was unable to commit data to a drive, a Web server shut down, or a user attempted to log on but failed to provide the correct password. This information is useful because it can help isolate or predict system failures. For example, if the SCSI driver event is frequently showing up in a log, you might have a SCSI card or drive that is about to fail. Such preemptive foresight can prevent a catastrophe.

Programs and OS components don't automatically make event entries in the log. The programmer or systems administrator tells the OS component or program to create event-log entries. Thus, you can adapt or write code to generate new event-log entries.

Before you can query your machines' log files to spot potential problems or write new event-log entries in those files, you need to understand how the logging process works. In addition, you need to understand the various elements that make up an event-log entry.

Understanding Event Logs
A typical Windows 2000 or NT machine has at least three different event logs: Application, Security, and System. (In Win2K, you might also have other logs, such as the DNS Server log.) When an OS component or program submits, or reports, an event, it specifies the event source for that event. For example, if the DHCP service reports an event, it specifies DHCPServer as the event source. The OS looks up DHCPServer in the registry and finds that this event source is registered to the System event log. The OS then stores the entry in the System event log and lists DHCPServer as the event source.

Each event source maps to a message table (usually a .dll file) that contains canned messages. For example, a Web service might have a message table that contains several numbered messages. Message 1 might be The web server has been started, message 2 might be The web server has been stopped, and so on. The numeric value associated with the message is the event ID. When a program or OS component reports an event, it identifies the event ID. When you run eventvwr.exe, the event ID specifies the message to display from the message table.

You might notice that, in some log events, the information changes from entry to entry. For example, one entry might say The web service was stopped at 11:28 am and another entry might say The web service was stopped at 3:14 pm. The text in a message table can include variables that are expanded when you view the text. The data for these variables is called the event's strings. Such variables are referred to as string 1, string 2, and so on.

When a program or OS component reports an event, it identifies the event type. There are five different event types:

  • EVENTLOG_ERROR_TYPE (indicates that an error such as a hardware or software failure has occurred)
  • EVENTLOG_WARNING_TYPE (provides a warning that something has occurred worth noting, but the occurrence isn't catastrophic)
  • EVENTLOG_INFORMATION_TYPE (provides information that the software thinks is of interest, such as a service has started)
  • EVENTLOG_AUDIT_FAILURE (indicates that a security-based audit has failed, such as a user failed to log on because of an incorrect password)
  • EVENTLOG_AUDIT_SUCCESS (indicates that a security-based audit has succeeded)

Querying Event Logs
The Perl Win32::EventLog extension provides all the functions you need to interact with event logs. This extension lets you read events, report events, and perform routine maintenance on the logs, such as backing up and clearing logs. Win32::EventLog comes with ActivePerl, which you can download from ActiveState Tool (http://www.activestate.com).

The script Query.pl uses Win32::EventLog to access and query event logs. Query.pl uses the Win32::EventLog object to display event log entries of type EVENTLOG_ERROR_TYPE, EVENTLOG_WARNING_TYPE, and EVENTLOG_INFORMATION_TYPE. This script is great for quickly obtaining a dump of recent event log activity on a machine. Listing 1 contains an excerpt from Query.pl. You can find the entire Query.pl script as well as the other scripts discussed here in the Code Library on the Windows Scripting Solutions Web site (http://www.winscriptingsolutions.com). These scripts work on all versions of Win2K and NT.

When you run Query.pl, you pass in three or more parameters. The first parameter specifies the name of the machine that contains the event logs you want to query. The second parameter represents the number of days that you want to go back into the event logs. The remaining parameters specify the event logs (e.g., Application, System) you want to examine. For example, if you want to display the Application and System event-log entries for the past 2 days on the machine \\server, type:

perl query.pl \\server 2 system application

at the command line.

Query.pl uses the new() function to create an instance of the Win32::EventLog object. The new() function has the syntax

Win32::EventLog->new
   ( $LogName \[, $Server \] )

where $LogName is the name of the event log (e.g., Application, System) to query. If this log is on a remote computer, the optional second parameter $Server specifies that computer's name.

After creating the Win32::EventLog object, the script uses that object's Read() method to read the event logs. As the first line of code at callout A in Listing 1 shows, you pass in flags ($Flag), an event-log record number (0), and a hash reference ($Log) to the Read() method. Earlier in the script, the code set $Flag to EVENTLOG_BACKWARDS_READ | EVENTLOG_SEQUENTIAL_READ. The logical OR (|) operator between these two flags causes the Read() method to walk through the event log sequentially in reverse order (i.e., to read the most recent entry first). Because the script walks through the event log sequentially, you don't need to pass in a record number; hence, you pass in the value of 0.

The Read() method populates an array with hash keys and values representing a particular event-log entry. The second line of code at callout A in Listing 1 makes sure the entry's EventType value is one of the three desired event types.

The code at callout B in Listing 1 fetches the event-log entry's text message from the message table. If the extension is unable to retrieve the text message (which sometimes occurs with remote event logs), the script joins the string data so that the user can at least see some information.

Reporting Events
Reporting events is as useful as retrieving them. For example, scripts often report errors in text-based logs. However, monitoring services don't monitor text-based log files. If you have your scripts report errors in an event log, monitoring services will detect those errors and immediately notify someone about them. This example illustrates the event log's usefulness as a central repository for errors, warnings, and information.

The script Report.pl shows how to report events. When you report an event, you need to specify the relevant information. The possible event-log hash keys are

  • Computer—contains the computer that will receive the event-log entry (if no computer is specified, the Win32::EventLog object's computer value is used)
  • Source—contains the event source (e.g., DHCPServer, Netlogon)
  • EventType—contains the event type (e.g., EVENTLOG_ERROR_TYPE, EVENTLOG_WARNING_TYPE)
  • EventID—contains the event ID (if no EventID is specified, the value of zero is used)
  • Category—contains the category that corresponds to the event (optional key)

Report.pl (and Report2.pl, which I'll discuss shortly) submits event-log entries into a specified computer's event log. The script takes a series of parameters in this order: computer, event source, event type, event ID, and series of strings that make up the message. For example, the command

perl report.pl server
   "Active Server Pages"
   failure 529
   "The user wasn't able to log on"

submits an audit failure for the "Active Server Pages" event-log source (which registers with the Application log for machines with Microsoft IIS installed). The event will use the message table event ID 529 (assuming such an entry exists in the message table) and specify only one message string.

Here's how Report.pl works. The script first creates a hash with the event-log entry values. The script then creates an instance of the Win32::EventLog object and passes the hash to the Report() method, which creates the log entry.

If a message table uses multiple strings (e.g., string1, string 2), you need to pass in multiple strings when reporting an event. However, the Report() method doesn't support multiple strings in version 0.062 of Win32::EventLog. Although version 0.071 fixes this problem, you must use the null character ("\0") to separate multiple strings. A better solution is to use the WriteEventLog() function instead of the Report() method. The script Report2.pl uses the WriteEventLog() function to perform the same tasks as Report.pl. So, if you need to submit multiple strings, use Report2.pl rather than Report.pl.

You can use the reporting scripts Report.pl and Report2.pl many ways. You can call one of the reporting scripts from another script to write any errors that the calling script might encounter in an event log. You can even call a reporting script from a script to write a log entry that indicates that the calling script successfully executed. In addition, you can use a reporting script to log an event. Suppose you just replaced a SCSI drive; you can call Report.pl to make a note of this replacement in the event log when the SCSI drive boots up.

Report.pl and Report2.pl are intentionally simplistic so that you can easily see how to submit event-log entries. You can adapt these scripts to interact with other scripts and services to provide more hearty event-logging capabilities.

Monitoring Event Logs with WMI
Windows Management Instrumentation (WMI) is a cool new technology that you ought to become familiar with. WMI lets scripts connect to local and remote machines and query them for information about their event logs, services, hardware, and other mechanisms and processes. WMI comes with Win2K and Windows Millennium Edition (Windows Me) and is available for other Windows platforms. You can download the core WMI components for NT 4.0 and Windows 9x from http://msdn.microsoft.com/downloads/c-frame.htm?/downloads/sdks/wmi/default.asp.

Like the Win32::EventLog extension, WMI lets scripts display the entries in a machine's event log. Unlike the Win32::EventLog extension, WMI lets scripts monitor changes in an event log. Thus, an incredibly useful aspect of WMI is that it lets a script monitor new event-log entries; the script EventMon.pl performs this task. This script connects to a remote machine and displays the new event-log entries.

Listing 2 contains an excerpt from EventMon.pl. The code at callout A in Listing 2 highlights an exciting part of WMI: the moniker string. The moniker string "winmgmts:\{impersonationLevel=impersonate,(security)\}//$Machine/" procures the WMI COM object. In this string, winmgmts: tells Win32 that you want to access the WMI service on the machine that $Machine specifies. In this case, $Machine specifies a remote machine. If you want to access the local machine's WMI service, you place a period (".") in the $Machine variable. The \{impersonationLevel=impersonate,(security)\} portion of the string instructs Win32 to connect to the remote machine by using the credentials of the user running the script.

The code after callout A in Listing 2 tells the WMI service to query all event-log entries submitted to the remote machine. The code uses the SQL query "select * from __instancecreationevent where targetinstance isa 'Win32_NTLogEvent'". This query looks for all (*) instances of a newly created event (__instancecreationevent) that is a Win32 event log (isa 'Win32_NTLogEvent').

If all goes well, the WMI service queues the new log events until the script fetches them with the code

my $Event =
   $Events->NextEvent( $TIMEOUT );

This code uses the WMI COM object's NextEvent() method to retrieve the new log events. The value that you pass to this method ($TIMEOUT) specifies how many milliseconds you want to wait for an event. If the time passes without the NextEvent() method retrieving any event, the method generates an error. The error's HRESULT value is 0x80043001, which signifies a timeout condition. If the NextEvent() method is successful, it returns a WMI Win32_NTLogEvent object. The script then uses this object to print out the various properties of that log event entry.

To run EventMon.pl, you type

perl eventmon.pl server

at the command line, where server is the name of the remote machine you want to access.

Just for fun, run EventMon.pl and let it continue to display new events. Then, open another DOS window and run Report.pl to submit an event-log entry. After the new event-log entry has been submitted, you'll see the entry displayed in the output of EventMon.pl.

Not So Hard After All
As I've demonstrated, event logs are not too difficult to query and adapt. You just need to know how the event log works and how to use such tools as the Win32::EventLog extension, the WriteEventLog() function, and WMI. With a bit of experimentation, these tasks will become quite easy and manageable.