Call MOM

Instrument and Monitor Your ASP.NET Apps Using WMI and MOM 2005

Michael Jurek

This article discusses:

  • The architecture of WMI
  • Building an ASP.NET module that generates WMI events
  • Creating a MOM management pack that monitors instrumented ASP.NET applications
This article uses the following technologies:

Code download available at:WMI.exe(195 KB)


Understanding WMI
Instrumenting an ASP.NET Application
Plugging into ASP.NET
Developing the Module
Deploying the Module
Testing the WMI Module
Using Microsoft Operations Manager
Creating a Custom Management Pack
Measuring Performance Rules
Watching the Results

The Microsoft® Dynamic Systems Initiative (DSI) provides a roadmap for simplifying enterprise software development, deployment, and management. Current Microsoft products and technologies that support the DSI model for system management include Automated Deployment Services, Microsoft Operations Manager (MOM) 2005, Systems Management Server (SMS) 2003, Virtual Server 2005, and Windows Server™ Update Services. Future technologies expected to extend the DSI model include Visual Studio® 2005 Team System and the System Definition Model (SDM) planned for a future version of Windows Server. (For more information about DSI, see Dynamic Systems Initiative.)

With these key DSI and SDM deliverables planned for the future, developers need manageable apps right now. Fortunately, the current version of Windows® Management Instrumentation (WMI) is able to satisfy many manageability requirements. In this article I'll demonstrate how WMI provides a first step towards DSI-style system management. I will develop a WMI-aware monitoring solution you can use to instrument your ASP.NET applications, and I'll introduce you to the capabilities of MOM 2005 that allow you to then monitor these instrumented applications.

Understanding WMI

WMI is the Microsoft implementation of Web-Based Enterprise Management (WBEM), and it has been an integral component of the Windows operating system since Windows 2000 (see Alan Boshier's MSDN®Magazine article on WMI at Windows Management Instrumentation: A Simple, Powerful Tool for Scripting Windows Management). WMI is an object-oriented repository that provides data about managed hardware and software. Central to WMI is the Common Information Model Object Manager (CIMOM) and a storage area called the CIM repository. CIMOM offers a unified programming interface for accessing data stored in the repository. CIMOM also offers a plug-in model for providers that expose objects to be managed. The operating system includes a number of providers allowing access to and management of hardware drivers, event logs, registry keys, performance counters, network settings, threads, processes, and so on (see Figure 1). Also, many Windows Server System technologies expose their management interfaces through WMI, including Application Center, BizTalk® Server, Operations Manager, SQL Server™, and Systems Management Server.

Figure 1 WMI Architecture

Figure 1** WMI Architecture **

Objects in the CIM repository are organized in logical containers called namespaces. Core operating system classes use the default namespace root\cimv2. Other managed products create their own namespaces. For example, BizTalk Server uses root\MicrosoftBizTalkServer. Each namespace owns metadata information in the schema, defining object classes and their properties, methods, and relationships. For example, every process in the operating system is represented by an instance of the Win32_Process class in the root\cimv2 namespace. The Win32_Process class has many properties, such as Name, Caption, ExecutablePath, and MinimumWorkingSetSize. It has also methods, such as Terminate, GetOwner, and SetPriority.

From the perspective of a database developer, a namespace might look like a database, since classes are table definitions, instances of a class are rows, object properties are columns, and relationships are joins between tables. WMI Query Language (WQL), a SQL-like language, lets you query objects in the CIM repository.

The CIM repository supports three kinds of events: extrinsic, intrinsic, and timer. Extrinsic events are explicitly fired by the application; examples include a power state change and the start of the shutdown process. Intrinsic events resemble database triggers; they are fired whenever managed objects are created, modified, or deleted. And timer events fire when a timer goes off.

WMI provides COM-based interfaces that allow access to all the data it holds, and these COM interfaces are frequently used in scripting environments to manage computers through automated scripts. The Microsoft .NET Framework provides a more convenient option. The System.Management namespace offers a layer on top of that COM functionality and makes heavy use of .NET Framework constructs such as delegates, iterators, and collections (see Zina Posen's article on WMI and .NET at WMI and .NET: System.Management Lets You Take Advantage of WMI APIs within Managed Code).

Most often, you will use the ManagementObject class in this namespace to access a specific object in the repository. The ManagementObjectSearcher class has facilities for querying repository data and iterating through collection of results. And finally, the ManagementEventWatcher class can listen to WMI events and fire .NET Framework events in response. If you want your application to act as a WMI provider, offering management data and firing events, you will use objects from the System.Management.Instrumentation namespace. Management objects and events are created by inheriting from Instance and BaseEvent classes, respectively, or by decorating class definitions with InstrumentationClassAttribute.

Instrumenting an ASP.NET Application

There is a wealth of information about an ASP.NET application that you might want to instrument and monitor. My solution will take advantage of some of that data in order to implement the following list of functionalities:

  • Fire an event whenever an unhandled exception occurs during request processing.
  • Fire an event whenever request processing takes longer than a predefined period of time.
  • Provide the current number of available worker and I/O threads in the thread pool (which can be used as one factor in evaluating an application's health).
  • Provide the current number of active anonymous and authenticated users (an active user is defined here as a request from a unique IP address occurring within the last five minutes).

This is just a random selection of the types of information that can be obtained from the current HttpContext information and other sources, so you're really only limited by your imagination in terms of the quality and quantity of data you expose. For example, I could measure the number of unique users based on unique cookie values, the number of requests made using legacy browsers, the frequency of requests coming from the most active IP address (a possible indication of Denial of Service attacks), the number of requests coming from specific network subnets, the number of requests exceeding predefined content length, and so on.

All of my instrumentation requirements can be fulfilled with WMI. I'll define ASPNET_ErrorEvent and ASPNET_ExcessivelyLongRequestEvent event classes that will be fired whenever the corresponding condition occurs. In addition, I will create an ASPNET_Counters class that contains my ASP.NET performance counters and statistics data.

Plugging into ASP.NET

The ASP.NET architecture allows for very modular systems, making it easy to integrate WMI monitoring into ASP.NET applications. Before processing an incoming request, the ASP.NET runtime creates an instance of the HttpContext class, which provides access to the incoming HttpRequest, to the outbound HttpResponse, and to a variety of other important objects. This context is passed through a series of pipeline stages, the most important being the actual processing endpoint—requests are routed to objects whose classes implement the IHttpHandler interface (see the diagram in Figure 2). For example, any requests for ASP.NET pages are processed by the System.Web.UI.PageHandlerFactory class, which given an HttpContext returns an appropriate IHttpHandler. The IHttpHandler then processes the request through its ProcessRequest method. There are other important stages in the request pipeline: BeginRequest, AuthenticateRequest, AuthorizeRequest, and EndRequest, to name a few.

Figure 2 ASP.NET Processing Pipeline

Figure 2** ASP.NET Processing Pipeline **

ASP.NET is extensible through modules, special components implementing the IHttpModule interface. These modules are configured in a dedicated section of the Web.config file for every ASP.NET application, and in the Machine.config file for the entire computer. Before processing an incoming request, modules are instantiated and given a chance to register hooks for pipeline processing stages. This mechanism is used by a handful of ASP.NET modules in the .NET Framework (see George Shepherd's ASP Column where he presents a list of modules and a description of their interaction).

IHttpModule is the basis for many ASP.NET infrastructure extensibility projects, including my WMI instrumentation module. The module registers event handlers for the BeginRequest, EndRequest, and Error events. In this way, the module is notified of every unhandled exception and can also measure the time interval between BeginRequest and EndRequest events, as well as updating statistics as necessary.

This approach has a couple of advantages. The module will be absolutely non-intrusive and orthogonal to any ASP.NET applications and extensions. Its installation is a simple two-step process. The first step is to copy the compiled module to the instrumented server; it either can be copied to the bin folder of every application or can be installed into the Global Assembly Cache (GAC) for sharing between applications. The second step is to register the module in a configuration file—either application-wide in a Web.config file or system-wide in a Machine.config file. Optionally, you can specify maximum tolerable request duration in either file. As a result, you can start monitoring of an ASP.NET application without any downtime; you won't even need to recompile it. For information specific to your applications, you can of course customize this solution as appropriate.

Developing the Module

To start writing the code, first specify the WMI namespace for the managed classes by adding a single InstrumentedAttribute to the assembly metadata (typically done in AssemblyInfo.cs):

using System.Management.Instrumentation; [assembly:Instrumented(@"root\MsdnMagazine\InstrumentedHttpModule")]

Creating a WMI-managed class is trivial; see Figure 3 for an example of a WMI-managed class called ASPNET_ExcessivelyLongRequestEvent. There are only two remarkable things in the code. First, there is an optional ManagedNameAttribute attached to the class, which defines class name in WMI. If you omit this attribute, the WMI class name will be the same as a .NET Framework class name. As there are slightly different naming conventions in each environment, you should rename the classes.

Figure 3 ASPNET_ExcessivelyLongRequestEvent

using System; using System.Management.Instrumentation; namespace MsdnMagazine.InstrumentedHttpModule { [ManagedName("ASPNET_ExcessivelyLongRequestEvent")] public class AspNetExcessivelyLongRequestEvent : BaseEvent { private string applicationNameValue; public string ApplicationName { get { return applicationNameValue; } } private int durationValue; public int Duration { get { return durationValue; } } private string requestPathValue; public string RequestPath { get { return requestPathValue; } } public AspNetExcessivelyLongRequestEvent( string applicationName, int duration, string requestPath) { applicationNameValue = applicationName; durationValue = duration; requestPathValue = requestPath; } } }

The second thing to note in the code is that the class inherits from a BaseEvent class defined in the System.Management.Instrumentation namespace, making the class WMI-manageable. As I mentioned earlier in the article, this is one of two ways to define WMI event classes. I have chosen the inheritance approach over the attribute approach because it leads to more elegant code. The other two managed classes, ASPNET_ErrorEvent and ASPNET_Counters, have very similar definitions and can be found in the code sample download accompanying this article.

The core module is shown in the code in Figure 4. There are six static fields: the name of the ASP.NET application, counters for anonymous and authenticated users, the threshold for excessively long requests, a managed class for the ASP.NET counters (not to be confused with Windows NT® performance counters), and a timer for refreshing counters.

Figure 4 Core Code of InstrumentedHttpModule

using System; using System.Web; using System.Threading; using System.Configuration; using System.Management.Instrumentation; namespace MsdnMagazine.InstrumentedHttpModule { public class HttpModule: IHttpModule { private static string applicationName; private static ExpiringUniqueStringCounter anonymousUsers; private static ExpiringUniqueStringCounter authenticatedUsers; private static int excessivelyLongRequestThreshold=2000; private static AspNetCounters counters; private static Timer refreshTimer; static HttpModule() { applicationName = HttpContext.Current. Request.ApplicationPath.Replace("/",""); anonymousUsers = new ExpiringUniqueStringCounter( TimeSpan.FromMinutes(5)); authenticatedUsers = new ExpiringUniqueStringCounter( TimeSpan.FromMinutes(5)); // If present, read long request threshold from config file if (ConfigurationSettings.AppSettings[ "ExcessivelyLongRequestThreshold"] != null) excessivelyLongRequestThreshold = int.Parse( ConfigurationSettings.AppSettings[ "ExcessivelyLongRequestThreshold"]); // Initialize and publish counters counters = new AspNetCounters(applicationName); RefreshCounters(null); counters.Published=true; // Start periodic refresh of counters refreshTimer = new Timer(new TimerCallback(RefreshCounters), null, 1000, 1000); } static void RefreshCounters(object state) { int availableWorkerThreads, availableIOThreads; ThreadPool.GetAvailableThreads(out availableWorkerThreads, out availableIOThreads); counters.Update(authenticatedUsers.Count, anonymousUsers.Count, availableWorkerThreads, availableIOThreads); } public void Init(System.Web.HttpApplication context) { context.Error += new EventHandler(context_Error); context.BeginRequest +=new EventHandler(context_BeginRequest); context.EndRequest += new EventHandler(context_EndRequest); } public void Dispose() {} private void context_Error(Object sender, EventArgs e) { Exception ex = HttpContext.Current.Server. GetLastError().InnerException; string typeName = ex.GetType().FullName; string message = ex.Message; string requestPath = HttpContext.Current.Request.Path; string details = ex.ToString(); new AspNetErrorEvent(applicationName, typeName, message, requestPath,details).Fire(); } private DateTime startTime; private void context_BeginRequest(object sender, EventArgs e) { startTime = DateTime.Now; } private void context_EndRequest(object sender, EventArgs e) { // Increment corresponding counter string ipAddress = HttpContext.Current.Request. ServerVariables["REMOTE_ADDR"]; if (HttpContext.Current.Request.IsAuthenticated) authenticatedUsers.Add(ipAddress); else anonymousUsers.Add(ipAddress); // Fire excessively long request event if necessary int duration = (int) DateTime.Now.Subtract( startTime).TotalMilliseconds; if (duration > excessivelyLongRequestThreshold) { string requestPath=HttpContext.Current.Request.Path; new AspNetExcessivelyLongRequestEvent(applicationName, duration,requestPath).Fire(); } } } }

User counters are instances of the ExpiringUniqueStringCounter class (see the accompanying code sample download). This class holds a private Hashtable of strings and the time of their last insertion. Upon retrieving the Count property, entries older than the predefined interval are removed and the count of remaining table entries is returned.

ASPNET_Counters class is a static member of the module, and therefore you will have one instance of this class per application domain. It is initialized and published to WMI in the module's static constructor that also starts a timer which updates these counters once per minute using the output value of ThreadPool.GetAvailableThreads and the Count property of the ExpiringUniqueStringCounter instances.

The module has to implement Init and Dispose methods defined in the IHttpModule interface. While Dispose has no role in this implementation (there aren't any unmanaged instance resources to release), Init is essential for the functioning of the module. Init is called at the beginning of every request and is the place where the module registers for the BeginRequest, EndRequest, and Error events of HttpContext.

BeginRequest's only task is to record the start time of the request processing. EndRequest has more things to do. It retrieves the request IP address from the HttpContext and passes it to either the anonymous or authenticated counter. EndRequest also calculates the time passed from start time and, if the interval is longer than the threshold, it collects the necessary data and fires ASPNET_ExcessivelyLongRequestEvent. The Error event handler has an expected role; it collects any necessary data about the exception and the environment and fires ASPNET_ErrorEvent.

Deploying the Module

As I mentioned earlier, the module either has to be copied into the /bin folder of every ASP.NET application using that module or it must be placed in the GAC. Since this module typically will be shared by many ASP.NET applications, I prefer to use the GAC, registering the assembly using gacutil.exe. like so:

gacutil /i MsdnMagazine.InstrumentedHttpModule.dll

The WMI schemas of the three instrumented object classes must be registered with CIMOM. When the instrumented class is first published or an instrumented event is first fired, the corresponding schema is automatically published by the .NET Framework runtime, if the process hosting the runtime has local administrative privileges. However, ASP.NET applications often run under a Network Service account that does not have local admin privileges; therefore you will need to register these schemas manually. Luckily, the .NET Framework contains the DefaultManagementProjectInstaller class, capable of registering WMI schemas. To use it, add a class to the assembly that inherits from the DefaultManagementProjectInstaller and is attributed with the RunInstallerAttribute, as shown in the following code:

using System.ComponentModel; using System.Management.Instrumentation; namespace MsdnMagazine.InstrumentedHttpModule { [RunInstaller(true)] public class WmiSchemaInstaller : DefaultManagementProjectInstaller {} }

The easiest way to run the installer is through the InstallUtil.exe utility, which uses reflection to iterate through all classes, running the Install method on all classes derived from the System.Configuration.Install.Installer class and properly attributed with RunInstaller. (Note that in the previous example, WmiSchemaInstaller derives from DefaultManagementProjectInstaller which in turn derives from Installer.) This utility is easy to use:

installutil MsdnMagazine.InstrumentedHttpModule.dll

Finally, the following XML fragment, when added to Web.config, will register the module for ASP.NET pipeline processing and will set up the configurable threshold:

<appSettings> <add key="ExcessivelyLongRequestThreshold" value="2500"/> </appSettings> <system.web> <httpModules> <add type="MsdnMagazine.InstrumentedHttpModule.HttpModule, MsdnMagazine.InstrumentedHttpModule, Version=, Culture=neutral, PublicKeyToken=8ea5a67752f3a4dd" name="InstrumentedHttpModule" /> </httpModules> </system.web>

You can add this fragment to the Web.config file of every ASP.NET application to be instrumented, or you can enable the functionality for the whole machine by adding the configuration entries to the Machine.config file.

Testing the WMI Module

You can use many tools based on WMI technology, such as the WMI Administrative Tools or the WMI Server Explorer Extension, which is integrated into Visual Studio .NET. However, you can learn more by creating a custom monitoring console that tests the ASP.NET application instrumentation created earlier. Subscribing to the management events raised by the instrumentation is simple:

ManagementEventWatcher errorWatcher = new ManagementEventWatcher( @"\\.\root\MsdnMagazine\InstrumentedHttpModule", "SELECT * FROM ASPNET_ErrorEvent"); errorWatcher.EventArrived += new EventArrivedEventHandler(DumpEvent); errorWatcher.Start();

All you have to do is to create an instance of ManagementEventWatcher, then set up the name of the monitored computer (the dot in the path string indicates a local computer) combined with the WMI namespace and WQL query selecting monitored events, and finally wire up the EventArrived event of the watcher. The event handler is rather simple—it just dumps names and values of all properties of the arrived event to the console output stream:

Console.WriteLine(managedObject.SystemProperties["__CLASS"].Value); foreach(PropertyData property in managedObject.Properties) { Console.Write("\t" + property.Name + " = " + property.Value); }

The code for listing all the instances of ASPNET_Counters is only a little bit more complicated—just create an instance of ManagementObjectSearcher, get its enumerator, iterate through the collection of returned objects, and dump their properties using the same code as I just showed:

using(ManagementObjectSearcher searcher = new ManagementObjectSearcher( @"\\.\root\MsdnMagazine\InstrumentedHttpModule", "SELECT * FROM ASPNET_Counters")) using(ManagementObjectCollection c = searcher.Get()) using(ManagementObjectCollection.ManagementObjectEnumerator enumerator = c.GetEnumerator()) { while(enumerator.MoveNext()) { DumpManagementObject(enumerator.Current); } }

Figure 5 Simple ASP.NET Testing Application

Figure 5** Simple ASP.NET Testing Application **

I will simulate ASP.NET request activity using a simple ASP.NET application consisting of a single page (see Figure 5). The yellow buttons simulate long-running requests by calling Thread.Sleep, and the red buttons produce two types of unhandled exceptions. These error events immediately appear in the monitoring console together with the values of ASPNET_Counters. Spend some time playing with this testing application and watch the results in the console output. This type of monitoring can be taken even further by deploying MOM.

Using Microsoft Operations Manager

MOM 2005 provides operational monitoring of server environments that support WMI. MOM is a scalable, distributed monitoring solution (see Figure 6). Agents can be deployed automatically or manually onto managed computers in order to collect data, watch for events, and execute response actions. Agents retrieve data, including event logs and performance counters, through various providers. Collected data is forwarded to the MOM server for storage in the central SQL Server database. Administrators have at their disposal an MMC console for defining rules, events, and actions. System operators can use a Windows Forms-based application for browsing through collected data and resolving or escalating alerts.

Figure 6 MOM 2005 Architecture

Figure 6** MOM 2005 Architecture **

An important part of MOM is a data store called the System Center Data Warehouse (SCDW). This is a classic star-schema database that may be periodically loaded with data from an operational database using SQL Server 2000 Data Transformation Services (DTS) technology. Collected data may be accessed and visualized using a variety of tools, such as SQL Server 2000 Reporting Services. (Note that MOM 2005 Workgroup Edition does not contain the SCDW.)

MOM management packs are carefully packaged expertise for a specific product or technology, such as Active Directory®, Exchange Server, SQL Server, and many others. Lists of management packs available from Microsoft and other providers can be found at Management Pack and Product Connector Catalog. From a technical point of view, a management pack is nothing more than a collection of rules, scripts, operator console views, and data warehouse reports. The real value of a management pack is its contained knowledge. The majority of management packs are created by the developers of the particular product. According to the Common Engineering Criteria, every new product in the Microsoft Windows Server System family will be accompanied by a management pack at launch time.

Creating a Custom Management Pack

While preconfigured management packs will be available for many commercial enterprise services, MOM also provides functionality that enables you to customize these solutions for your specific needs. In fact, you can even create customized management packs to support specific business needs or your own applications.

The next task will be creating a prototype management pack for the ASP.NET monitoring module developed earlier. This part is not intended to be a step-by-step guide. It contains only basic guidelines and offers you a lot of space for experimentation. This management pack should be regarded as a rather simplistic prototype; you could certainly go further and fine-tune it. (A complete prototype of a management pack is part of code download accompanying this article.) See Monitor Your .NET Applications with Microsoft Operations Manager 2005 for a more detailed discussion of management pack authoring.

First, open the MOM Administrator Console, which is shown in Figure 7, and start creating rules. The first step is creating a Computer Group that will serve as a target for rules in Management Packs/Computer Groups console node. This group can be created either by using a formula based on computer attributes or by explicit inclusion of the computer in the group.

Figure 7 Event and Alert Rules in MMC Console

Formulas require registry-based computer attributes, which are not handy for this scenario. Instead, create a computer group named ASP.NET Instrumented Module and explicitly add all computers with instrumented ASP.NET applications to this group. Now you can create a new rule group, also named ASP.NET Instrumented Module, in the Management Packs/Rule Groups console node. This new rule group will be associated with the computer group of the same name and serves as a container for all the rules that you'll create later.

Next, create two rules for collecting instances of ASPNET_ErrorEvent and ASPNET_ExcessivelyLongRequestEvent, using the wizard provided. Specify the rule type as Collect, and leave all the values at their defaults with the exception of instructing MOM to store all event parameters. The only tricky part is the configuration of a data provider. You have to create a new one, using the WMI Events type. Use the following query:

SELECT ApplicationName, Duration, RequestPath FROM ASPNET_ExcessivelyLongRequestEvent

Do the same for ASPNET_ErrorEvent. After committing configuration changes in the MMC console and waiting for the changes to propagate, MOM should start collecting the custom events. This can be checked by raising these events in the ASP.NET test apps and seeing them in Events view of the Operator Console.

To help raise problem issues to operators, MOM has rules which create "Alerts." These rules are labeled Alert On or Respond To Event. Defining these rules is pretty straightforward as you can reuse data providers from the previous step. Only the alert page requires modification. Just select Generate Alert checkbox and set alert severity to red (Critical Error) for ASPNET_ErrorEvent and yellow (Warning) for ASPNET_ExcessivelyLongRequestEvent. Note that this type of rule will also collect events, so if you create an alerting rule, no collection rule is required.

After committing the configuration, MOM should raise alerts when the events occur. Individual events leading to a specific alert can be seen in Events tab of alert details.

If you have a working SMTP messaging infrastructure, you can e-mail a specific group of administrators about critical problems. First you must create a notification group called ASP.NET Instrumented Module Administrators, then create a new operator and add this operator to the group. Create an alert rule that responds to all alerts of Critical Error severity by sending an e-mail message to this notification group. After committing and propagating changes, every new alert of Critical Error severity should result in an e-mail delivered to the operator's mailbox.

Measuring Performance Rules

To collect performance data from monitored computers, performance rules must be created in the Administrator Console. The only non-trivial part is defining a data provider (see Figure 8 for an example). Create four rules for collecting your counters (AvailableWorkerThreads, AvailableIOThreads, UniqueAnonymousUsers, and UniqueAuthenticatedUsers). The prototype samples data at one-minute intervals, generating a huge amount of information. In a production environment, it's probably better to increase this interval to 15 minutes or so. Shortly after these changes are made, you'll be able to review the performance values in the Operator Console.

Figure 8 Numeric Provider Properties

Figure 8** Numeric Provider Properties **

Performance data values that are too high or too low may indicate problems in the monitored systems. It is not surprising that MOM offers alerts based on these data values. In the prototype, I created two threshold performance rules that generate yellow warning alerts when the value of the AvailableIOThreads or AvailableWorkerThreads counter drops below 5. You can reuse data providers from the previous step, set up thresholds, generate alerts of warning severity, and commit changes. To test these rules, create a testing page that synchronously waits for some external operation and overload the application using a stress tool, such as Application Center Test.

Watching the Results

At this point you have created a basic set of rules for collecting and reacting to data produced by the instrumented module. The Operator Console offers many interesting views, but most of them are too general to effectively monitor your module. You will have to create some customized views for ASP.NET application operators.

You can create filtered views for events generated by the module. In the Operator Console, create a new folder named ASP.NET Instrumented Module and then create a new alerts view named Error Events. Events should be filtered using an event source- and description-matching regular expression, as shown in the following:

(Source='WMI') AND (Description LIKE '__CLASS=ASPNET_ErrorEvent*')

You can create a similar view for Excessively Long Request Events.

Next, create alert views for the module. For example, you can create an All Unresolved Alerts view displaying all unresolved alerts in your computer group regardless of the management pack they originate from. This view is very important as it also tracks lower-level problems of managed ASP.NET computers such as low disk space, processor over-utilization, long kernel-mode HTTP request queues, and others, depending on the management packs you have installed and enabled. Other views might display only unresolved alerts triggered by ASPNET_ErrorEvent and ASPNET_ExcessivelyLongRequestEvent events, respectively. Filter expressions for these three views can be expressed like this:

(ComputerGroup='ASP.NET Instrumented Module') AND (ResolutionState<>'Resolved') (Source='WMI') AND (ResolutionState<>'Resolved') AND (Description LIKE '__CLASS=ASPNET_ErrorEvent*') (Source='WMI') AND (ResolutionState<>'Resolved') AND (Description LIKE '__CLASS=ASPNET_ExcessivelyLongRequestEvent*')

A third view type is the performance view. Here I've created three views of Performance Data View type that are very handy for a quick view of performance data in either tabular form or a chart (see Figure 9). The first view shows unique users of both anonymous and authenticated type for every computer and every ASP.NET application. The second view does the same for the number of available worker and I/O threads. The third view displays processor utilization for every computer in the computer group (this view relies on data collected by rules from Windows Base Operating System management pack). Symbolic filter expressions for all three views are as follows:

(Object='root\MsdnMagazine\Instrumented HttpModule:ASPNET_Counters') AND (Counter LIKE 'Unique*') (Object='root\MsdnMagazine\Instrumented HttpModule:ASPNET_Counters') AND (Counter LIKE 'Available*') (Object='Processor') AND (Instance='_Total') AND (Counter='% Processor Time') AND (ComputerGroup='ASP.NET Instrumented Module')

Figure 9 Numeric Performance Data in Operator Console

As I mentioned earlier in this article, data from the MOM operational database is regularly moved to a star-schema data warehouse for analysis and reporting. Data is transferred using SQL Server DTS technology, with all the dirty work done by the MOM.Datawarehousing.DTSPackageGenerator.exe utility. This utility has a handful of command-line switches for creating and running DTS packages. If all the databases physically reside on a MOM computer and have their default names, you may run this utility without command-line parameters—the utility just transfers the data and exits.

Now you can build online analytical processing (OLAP) cubes from collected data or create reports using SQL Server 2000 Reporting Services. Predefined reports that come with management packs are based on SQL Reporting Services and can be easily accessed through the Web-based Reporting Console. Custom reports are designed in Visual Studio .NET. For an excellent introduction to report building, see the article by John C. Hancock at Reporting: Deliver User-Friendly Reports from Your Application with SQL Server Reporting Services or see SQL Server 2000 Reporting Services for general information. I also recommend taking a look through the Report Definition Language (RDL) for predefined reports. Figure 10 provides an example of a custom parameterized report showing the number of unique users over a period of time.

Figure 10 Charting Collected Data in Reporting Console

An exported management pack and report project can be found in the code download. What I've discussed here is far from a complete solution for monitoring ASP.NET applications, so feel free to take this prototype, extend it, and fine-tune it to suit your needs.

Michael Jurek is a Software Architect for Microsoft in the Czech Republic, Prague, with a specialization in distributed systems architecture and integration. Before joining Microsoft, he worked as a scientist and a trainer of Microsoft server-side technologies. Michael can be contacted at