Health Monitoring in ASP.NET 2.0: Raising Custom EventsBy Scott Mitchell
As discussed in previous articles in this article series, ASP.NET 2.0's Health Monitoring system is designed to monitor the health of a running ASP.NET application in a production environment by recording event information to a specified log source. The Health Monitoring system includes a plethora of pre-defined events and the ASP.NET runtime will automatically raise certain events during the course of an application's lifetime. However, there may be times when we need to raise these events programmatically through our own code. Moreover, we can create our own custom events for scenarios not already accounted for by the Health Monitoring system.
In this article we will examine how to create a custom event and then how to programmatically raise it. As with other Health Monitoring events,
when the event has been raised the Health Monitoring system will consult the configuration information in
Web.config to determine
what log source(s) to record the event's details. For this article we will create a custom event and write code to record log in attempts on
a locked out user account. Read on to learn more!
Understanding the Problem...
ASP.NET 2.0's Membership system enables page developers to quickly and easily manage user accounts. See my Examining ASP.NET 2.0's Membership, Roles, and Profile article series for more detailed information on managing user accounts in ASP.NET 2.0. For security purposes, the Membership system will lock out a user account if there are more than a certain number of invalid login attempts within a certain window of time. Once a user has been locked out, they cannot log into the site until an administrator unlocks their account. Part 4 of the Examining ASP.NET 2.0's Membership, Roles, and Profile article series looks at how to record login attempts for locked out users into a custom database table than an administrator can review. This process provides administrators with a centralized place detailing the locked out user accounts and an interface to re-activate such users,
We can enhance the above process by also recording log in attempts on a locked out user account to the Health Monitoring system. Then, with a few configuration settings, an administrator could have such events automatically emailed to her. (See the Notifications via Email article in this series for more information on configuring Health Monitoring to log events via email messages.)
While the ASP.NET runtime records various authentication-level events by default, it does not record if a user who attempted to login but failed, failed because their account was locked out. Moreover, there is not a built-in Health Monitoring event designed to capture this information. Consequently, we have three tasks facing us:
- Create a custom event class that will be raised when a user attempts to log in via a locked out user account
- Raise this event when a user attempts to log in via a locked out user account
- Update the Health Monitoring configuration in
Web.configto log this custom event to one or more log sources
Creating a Custom Health Monitoring Event
All Health Monitoring events must derive from the
WebBaseEventclass. In the
System.Web.Managementnamespace you'll find that the
WebBaseEventclass has a variety of subclasses that break down different types of events into different classes. This type hierarchy is best expressed pictorially. The following diagram shows many (but not all) of the built-in Health Monitoring events. I've drilled down and shaded the events relating to security auditing, since those are the set of events pertinent to this article.
Since we want to have an event that logs when a user attempts to log in with a locked out user account, we could conceivably use the
class. However, it is possible to create your own Health Monitoring event classes by deriving them from one of the existing classes in the
hierarchy. While it may be a bit of overkill for this example, let's create our own event class that derives from the
record details about this type of event. Let's name this class
AttemptingToLogIntoLockedAccount. For less trivial examples, you'd
likely want to add additional properties or methods to this custom class to capture information not already encompassed by base classes. However,
this example is rather simple and we don't need to record any details not already recorded by the base class (
Consquently, the code for the
AttemptingToLogIntoLockedAccount class is very short:
Note that the
AttemptingToLogIntoLockedAccount class is derived from the
class via the
Inherits WebAuthenticationFailureAuditEvent. Also, the
AttemptingToLogIntoLockedAccount class defines
a single constructor that accempts as inputs a message, event source, and the name to authenticate (i.e., the name of the locked out user
account). The constructor simply calls the base class's constructor, passing in these values plus the event code.
As discussed in earlier articles in this series, each event is associated with an event code that describes the type of event in a numeric format.
The existing Health Monitoring events have pre-defined event codes. For custom events, we need to decide upon an event code that is greater than
the predefined value
value. For this class, I arbitrarily chose to use the event code of
WebEventCodes.WebExtendedBase + 5000.
The download available at the end of this article includes a working sample application. There, I placed the
in the application's
~/App_Code folder. Alternatively, you could create this class in a separate Class Library project and then
add that project as a refernece to your Web application.
Note: A C# version of a class similar to this one is available in the article How To: Instrument ASP.NET 2.0 Applications for Security.
Programmatically Raising the
At this point we have a suitable Health Monitoring event, but we need to write code in order to have this event raised. Raising a Health Monitoring event is easy: just create an instance of the event class and then call its
Raise()method. In my Examining ASP.NET 2.0's Membership, Roles, and Profile - Part 4 article, I have created an event handler for the Login control's
Authenticateevent. In this event handler I can determine if the logged on user's attempt at authentication failed and, if so, if it was because their account is locked out. In this case, I want to raise the
Raising this event is simply a matter of plugging in the following code into the appropriate spot in the Login control's
When creating the
AttemptingToLogIntoLockedAccount object we use "Attempting to Login to a Locked Out Account!!" as the event message,
the current ASP.NET Page instance (
Me) as the event source, and the username entered into the Login control as the name to authenticate.
We then call the object's
Raise() method. This instructs the Health Monitoring system to record the event to the log source(s) specified
Web.config to Record the
At this point, our application is not configured to record the
AttemptingToLogIntoLockedAccountevent to any log sources. Therefore, event when this code runs if a user attempts to log in using a locked out account, this event is not recorded anywhere. To remedy this, we need to update the application's configuration information. The following markup instructs the Health Monitoring system to record these events to the SQL Server log. Note the definition of the event "AttemptingToLogIntoLockedAccount Errors" for the type
<eventMappings>section along with the mapping of this event type to the "SqlWebEventProvider" log source in the
After setting up this configuration information, whenever a user attempts to log in with a locked out account, this attempt will be recorded in the Health Monitoring SQL Server database. In Part 1 of this article series I showed how this log could be displayed on a web page. The following screen shot from the sample application available at the end of this article shows the error log and notes the login attempt from Bruce, who has a locked out account.
The Health Monitoring system has several pre-defined events and the ASP.NET runtime automatically raises a number of these events during application execution. There may be times, however, when you need to create your own custom events or raise one of the existing events programmatically. Creating a custom event is easy: just have it derive from one of the existing Health Monitoring event classes. To raise an event (either a custom or built-in one), simply instantiate the appropriate class and then call its
Raise()event. Upon doing so, the Health Monitoring system will consult the application's configuration to determine how to log the event.