This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.
|
Using a Web Server for System Performance Monitoring and Management
Michael Swartzendruber |
How can a weasel help you keep an eye on system performance? The WEASEL (Web Enabled Access to System Event Logs) system introduced here shows how you can put system monitoring tools into ISAPI DLLs. |
There are lots of uses for Web-based software, from Jane Does home page to the ultrasophisticated e-commerce sites now cropping up. Id like to show you another interesting way you can put a Web server to work for you: as a system monitoring and management tool.
A Web server has many uses as a component of a network-based system management solution. Generally, network management tools are designed to monitor the health of servers and other hardware on an intranet. But if your intranet is complex you could end up needing many management tools to observe the network. Furthermore, most of these tools are full-blown applications, with limited utility for road warriors or off-site support staff. These days, its safe to assume that everyone has some form of Web browser on their machine. If you could Webify these management tools to take advantage of these browsers, then everyone could benefit. That is what this article is about. Specifically, Ill introduce the notion of using server-side code (in the form of ISAPI DLLs) to perform system monitoring and management tasks. These DLLs can be written to perform virtually any task, and can query any host on the networktheyre not limited to the local host. Since these code modules are implemented as server-side code on a Web hub, the user is no longer limited to using vendor-specific or proprietary management tools. All the user needs to monitor and manage any system is a Web browser like Microsoft Internet Explorer. By the time you complete this article, you should have enough information to start implementing your own solution for monitoring the servers on your intranet through a Web interface.
WEASEL
Splitting Logs
Once a connection to the Windows NT event log has been formed and the number of records to be read is known, its a simple exercise to enter a read loop to get each record in the log. Lest you be lulled into a sense of complacency, though, the real fun begins once you have a record in scope. Ill start my explanation at the CEventLog::ReadRecord method in CEventLog.cpp. The first thing you must account for is the fact that an event log record is a variable-size structure. For each record to be read, a "dummy read" must be issued to determine just how many bytes are required to hold the current event log record. The CEventLog::ReadRecord call performs this step by passing a value of 0 for the dwReadSize parameter. This invokes a call to the Windows NT API ReadEventLog, passing 0 for the size of the available output buffer. It essentially asks this API for the number of bytes required to hold the event log record. This is a common approach used by other Windows NT API calls that can return a variable length of data. Now that you know how many bytes the event log record requires, an appropriate amount of space can be allocated. Then you issue another call to the ReadEventLog API, this time passing the preallocated buffer and the size of this buffer. This triggers an actual read of the event log record, placing the data into the process space.
Here is Where Some of the Fun Begins
How Normal is This Data?
Where Did I Put My Keys?
|
|
As shown in Figure 7, the source name from the event log record points to one of the keys under this path. Each of these source keys contains a number of value entries. The ones that are most significant for now are CategoryMessageFile and EventMessageFile. The values for these entries indicate a file name containing a message string that is assigned to the event ID or category ID. In rough terms, these files have a set of string resources embedded in them. These resources have numeric IDs associated with them. Therefore, to get any particular message string, I need to know the file name and the string ID. In the case of the Windows NT event log, the file name is found in the registry key paths and the string IDs are found in the event log record. These two items together represent the key that I need to complete my preparation of the event log record for display. This work is done in the CEventLog::GetRecordInfo method in CEventLog.cpp. Here I invoke the actions of the CRegistry class, which provides wrapper services around the Windows NT registry. I use this class to connect to the registry as well as to read the values of certain keys. In this case I use it to extract the file names for the EventMessageFile and the CategoryMessageFile keys under the Source key that is indicated by the event log record. In some cases, there may not be full path information to the files of interest. Instead, the path may contain strings that look like this: |
|
Because of this, you have to either figure out the system root directory and tokenize this path yourself, or you can make a call to the little-known Windows NT API ExpandEnvironmentStrings, which converts the %SystemRoot% section of the path into the actual path for that value. Now, armed
with the full path, I can attempt to load the file containing the message string so that I can extract that string for
display purposes.
When I translated the EVENTLOGRECORD structure earlier, I copied a variable number of strings into the CEventLogRecord class. This is where they come back into use. Typically, the string loaded from the file will be a format string; in other words, it will have any number of %s arguments embedded in it for the purposes of a sprintf call. The strings that were stored in the EVENTLOGRECORD are the arguments to that sprintf call. Therefore, I have to form a va_arg-style arglist from this list of strings. Once I've done that, I can pass them off as an argument in a call to FormatMessage. This call gets the message string from the file indicated in the EventMessageFile value, performs the sprintf operation against that string and the variable number of arguments I pass to it, and presents the final display message. The rest of this function performs some error trapping, and will provide default messages for display should something fail. This scheme does present a bit of a problem: when you are probing the event log of a foreign host, the event log may indicate sources or DLLs that aren't loaded on the local host. This can cause this mechanism to fail because it can't load the message files for display. I must admit that I don't have that particular problem worked out yet.
Are We There Yet?
|
Figure 8: The Weasel Test Page |
The solution to this problem was implemented using HTML hidden variables. So the last task weasel.dll performs for each request is to store some context information that it returns to the user who made the request. After the event records have been retrieved and formatted, the DLL will store the parameters for the fetch in the reply page. These context variables are then wrapped in a simple form that the user can click to get the next set of records.
Hidden variables are very useful for this type of feature. And implementing them in this fashion prevented the problems I experienced in trying to shove too much data into the browser. This is also a good improvement because the user has the ability to page through the data in a manner that lets them analyze the log content in a piecemeal fashion, as opposed to being presented with the entire log at once.
Installation and Use
Wrapping Up
|
From the September 1998 issue of Microsoft Interactive Developer.