When you think ASP, think...
Recent Articles
All Articles
ASP.NET Articles
ASPFAQs.com
Message Board
Related Web Technologies
User Tips!
Coding Tips
Search

Sections:
Book Reviews
Sample Chapters
Commonly Asked Message Board Questions
JavaScript Tutorials
MSDN Communities Hub
Official Docs
Security
Stump the SQL Guru!
Web Hosts
XML
Information:
Advertise
Feedback
Author an Article

ASP ASP.NET ASP FAQs Message Board Feedback
 
Print this Page!
Published: Wednesday, June 27, 2007

Health Monitoring in ASP.NET 2.0: Raising Custom Events

By Scott Mitchell


A Multipart Series on ASP.NET 2.0's Health Monitoring System
The Health Monitoring system in ASP.NET 2.0 is designed to monitor the health of a running ASP.NET application in a production environment. This article is one of an ongoing series on the Health Monitoring system.

  • Health Monitoring Basics - explores the concepts and advantages of the Health Monitoring system and looks at logging events to a Microsoft SQL Server database.
  • Notifications via Email - looks at security-related events and shows how to alert an administrator to failed authentication attempts by "logging" events to email.
  • Raising Custom Events - learn how to create and raise custom Health Monitoring events.
  • (Subscribe to this Article Series! )

    Introduction


    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!

    - continued -

    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:

    1. Create a custom event class that will be raised when a user attempts to log in via a locked out user account
    2. Raise this event when a user attempts to log in via a locked out user account
    3. Update the Health Monitoring configuration in Web.config to log this custom event to one or more log sources
    The remainder of this article looks at tackling these three steps. If you have not already read Examining ASP.NET 2.0's Membership, Roles, and Profile - Part 4, please do so now, as this article borrows heavily from its code.

    Creating a Custom Health Monitoring Event


    All Health Monitoring events must derive from the WebBaseEvent class. In the System.Web.Management namespace you'll find that the WebBaseEvent class 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 existing WebAuthenticationFailureAuditEvent 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 WebAuthenticationFailureAuditEvent to 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 (WebAuthenticationFailureAuditEvent). Consquently, the code for the AttemptingToLogIntoLockedAccount class is very short:

    Imports System.Web.Management
    Imports Microsoft.VisualBasic

    Public Class AttemptingToLogIntoLockedAccount
        Inherits WebAuthenticationFailureAuditEvent

        Public Sub New(ByVal message As String, ByVal eventSource As Object, ByVal nameToAuthenticate As String)
            MyBase.New(message, eventSource, WebEventCodes.WebExtendedBase + 5000, nameToAuthenticate)
        End Sub
    End Class

    Note that the AttemptingToLogIntoLockedAccount class is derived from the WebAuthenticationFailureAuditEvent 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 WebEventCodes.WebExtendedBase 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 AttemptingToLogIntoLockedAccount class 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 AttemptingToLogIntoLockedAccount Event


    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 Authenticate event. 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 AttemptingToLogIntoLockedAccount event.

    Raising this event is simply a matter of plugging in the following code into the appropriate spot in the Login control's Authenticate event handler:

    Protected Sub Login1_LoginError(ByVal sender As Object, ByVal e As System.EventArgs) Handles Login1.LoginError
      ... A lot of code removed for brevity ...

      If userInfo.IsLockedOut Then
        'The user account is locked out!

        'Raise the AttemptingToLogIntoLockedAccount event
        'This will record this event via ASP.NET's Health Monitoring feature

        'First, create the event...
        Dim lockedOutEvent As New AttemptingToLogIntoLockedAccount( _
                      "Attempting to Login to a Locked Out Account!!", _
                      Me, _
                      Login1.UserName)

        'Now, raise it!!
        lockedOutEvent.Raise()

      End If

      ... Code removed for brevity ...
    End Sub

    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 in Web.config.

    Updating Web.config to Record the AttemptingToLogIntoLockedAccount Event


    At this point, our application is not configured to record the AttemptingToLogIntoLockedAccount event 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 AttemptingToLogIntoLockedAccount in the <eventMappings> section along with the mapping of this event type to the "SqlWebEventProvider" log source in the <rules> section.

    <configuration>
      <system.web>
        <healthMonitoring enabled="true">
          <eventMappings>
            <clear/>
            <!-- Log only AttemptingToLogIntoLockedAccount events -->
            <add name="AttemptingToLogIntoLockedAccount Errors" type="AttemptingToLogIntoLockedAccount" startEventCode="0" endEventCode="2147483647"/>
          </eventMappings>

          <providers>
            <clear/>
            <!-- Provide any customized SqlWebEventProvider information here (such as a different connection string name value -->
            <add connectionStringName="LocalSqlServer" maxEventDetailsLength="1073741823" buffer="false" name="SqlWebEventProvider" type="System.Web.Management.SqlWebEventProvider"/>
          </providers>

          <rules>
            <clear/>
            <add name="AttemptingToLogIntoLockedAccount Default" eventName="AttemptingToLogIntoLockedAccount Errors" provider="SqlWebEventProvider" profile="Default" minInstances="1" maxLimit="Infinite" minInterval="00:00:00" custom=""/>
          </rules>
        </healthMonitoring>

        ...
      </system.web>
    </configuration>

    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.

    Conclusion


    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.

    Happy Programming!

  • By Scott Mitchell


    Attachments


  • Download the code used in this article
  • Further Readings


  • Examining ASP.NET 2.0's Membership, Roles, and Profile - Part 4
  • How To: Instrument ASP.NET Applications for Security
  • How To: Use Health Monitoring in ASP.NET 2.0
  • A Multipart Series on ASP.NET 2.0's Health Monitoring System
    The Health Monitoring system in ASP.NET 2.0 is designed to monitor the health of a running ASP.NET application in a production environment. This article is one of an ongoing series on the Health Monitoring system.

  • Health Monitoring Basics - explores the concepts and advantages of the Health Monitoring system and looks at logging events to a Microsoft SQL Server database.
  • Notifications via Email - looks at security-related events and shows how to alert an administrator to failed authentication attempts by "logging" events to email.
  • Raising Custom Events - learn how to create and raise custom Health Monitoring events.
  • (Subscribe to this Article Series! )



    ASP.NET [1.x] [2.0] | ASPMessageboard.com | ASPFAQs.com | Advertise | Feedback | Author an Article