oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

Logging with Custom Web Events

by Jesse Liberty

Every serious ASP.NET application requires logging, though not every application gets the logging it requires. Keeping an automated log allows you to track exceptions, errors, and unexpected events. A log can be used to create an audit trail, to track changes to your database, and to monitor the health and usage of your system.

There is any number of ways to create a logging system, and there is any number of places to log to. Among the most popular methods are logging to a file, logging to a database and, at times, sending an email to IT support (or, worse, to the developer!) when something goes drastically wrong.

ASP.NET provides extensive support for logging with the Health Monitoring classes provided in the System.Management namespace, but few know of it, and fewer use it. My personal theory is that this underutilization is due to a perfect storm of three factors: the system is typically presented in all its off-putting complexity; it is declarative, which is not a "natural" way for C# or VB programmers to think about coding; and Microsoft led us astray by naming the system Health Monitoring.

In this article, I will not attempt to explain the Health Monitoring system. Instead, I will extract the nuggets you can use to create a minimal yet very effective logging system that will send email in a crisis and routinely log to a database, and that is robust and extensible so that you can add the functionality you need with little effort.

Examining the Log

Before going into the details of how to add logging to your program, let's examine the output we'd like to see. In the application we're going to build, we'll take advantage of just a couple of the possible features, and log all errors and some events to a SQL Express table. We'll send an email message whenever an exception is thrown that might lead to a mismatch between what the app saves to a database and what the user hoped for.

It turns out that when the tables are created for Forms security and personalization (see my previous article), an extra table is added to the ADPNETDB.MDB database file: aspnet_WebEvents_Events, which is incredibly handy for capturing (surprise!) Web Events--the backbone of our logging. The structure of this table is shown in Figure 1.

Web Events table
Figure 1. aspnet_WebEvents_Events table structure

Web Events are provided by the Framework, in a hierarchy of classes, all deriving from the base class WebBaseEvent. This base class holds the date and time of the event, a unique ID, and an event code and a detail code, as well as a text message.

The event code is an enumeration of errors that might arise during the life cycle of an application (e.g., "RuntimeErrorValidationFailure"), a list of which can be obtained in the documentation, but that does not concern us here since we'll be creating our own event codes. (EventDetailCodes are used to subdivide event codes.)

This information can also be packaged into an email message, as shown in the following snippet:

MyProject Warning!
** Summary **
This message contains events 1 to 1 from the total of 1 events scheduled for this notification.  There were 0 events left in the buffer at the beginning of this notification.

** Application Information **
Application domain: cfde81e7-1-128169905359687500 Trust level: Full Application Virtual Path: /My Application Path: D:\Test\MyApplication \ Machine name: BERNSTEIN

** Events **
Event code: 3005
Event message: An unhandled exception has occurred.
Event time: 3/26/2007 2:09:44 PM
Event time (UTC): 3/26/2007 7:09:44 PM
Event ID: a9224a59cb8b495b98dcd88740c79ecf Event sequence: 28 Event occurrence: 1 Event detail code: 0

Process information:
    Process ID: 4500
    Process name: WebDev.WebServer.EXE
    Account name: BERNSTEIN\Jesse

Exception information:
    Exception type: System.Data.OleDb.OleDbException
    Exception message: Data type mismatch in criteria expression.

Request information:
    Request URL: http://localhost:2573/MyApplication/MyPage.aspx
    Request path: /MyApplication/MyPage.aspx
    User host address:
    User: jesse
    Is authenticated: True
    Authentication Type: Forms
    Thread account name: BERNSTEIN\Jesse

Thread information:
    Thread ID: 8
    Thread account name: BERNSTEIN\Jesse
    Is impersonating: False
    Stack trace:    at  [stack trace here]

[footer message here]

Pages: 1, 2, 3, 4

Next Pagearrow