An Exception to the Rule, Part 2

 

Rob Howard
Microsoft Corporation

August 28, 2001

In the previous column, we briefly mentioned Application_Error events and Page_Error events. These events are raised whenever we have an unhandled exception occur within our application. If a Page_Error event is present within an ASP.NET page, and an unhandled error occurs, the event is raised. Application_Error is slightly different. It is raised for all unhandled exceptions in our ASP.NET application and is implemented in global.asax or within an HTTP Module.

Both of these events are extremely useful since they allow us a final opportunity to execute code before sending the caller an HTTP redirect to the named ASP.NET error page. Examples of their usefulness include logging the error to the Microsoft Windows® Event Log or sending e-mail to the administrator depending upon the nature of the exception.

Writing to the Windows Event Log

The Windows Event log is useful for recording application level errors. Using the .NET classes found in the namespace System.Diagnostics, we can easily write to the Windows Event log. Below is the implementation (in Visual Basic® .NET):

Imports System.Diagnostics

Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)

  Dim Message As String
  Message = "Url " & Request.Path & " Error: " & Server.GetLastError.ToString()

  Dim Log As New EventLog()
  Log.Source = "Application"
  Log.WriteEntry(Message, EventLogEntryType.Error)

End Sub

In the case above, we're accessing the exception through Server.GetLastError (more on that later) and storing the details in the Application log as type Error—this is approximately three or four lines of code.

If we would rather write to a custom event log, such as Web_Errors, we simply need to add some more code to create this alternative event log:

Dim LogName As String
LogName = "Web_Errors"

If (Not EventLog.SourceExists(LogName)) Then
  EventLog.CreateEventSource(LogName, LogName)
End If
'other code
Log.Source = LogName
'other code

In either case, when an exception occurs, we can view the details through the Windows Event log:

Figure 1. Windows Event log displaying an exception

In the Description section above, we see the URL where the exception was originally generated, along with the exception details providing line number and stack trace information about the exception.

Sending an E-mail

Instead of, or in addition to, logging the exception to the Windows Event log, we could also send an e-mail depending upon the severity of the exception. Again, the code is very straightforward:

Import System.Web.Mail

Sub Application_Error(sender as Object, e as EventArgs)
  Dim message as New MailMessage

    message.To = "rhoward@microsoft.com"
    message.From = "Commerce Application"
    message.Subject = "Critical Application Exception: " & Request.Path
    message.BodyFormat = MailFormat.Html

    message.Body = "<html><body><h1>" & Request.Path & "</h1>" &  
                         Server.GetLastError.ToString() & "</body></html>"

    SmtpMail.Send(MyMessage)

  End Sub

The above sample assumes that an SMTP server is available through localhost, otherwise the SmtpMail.SmtpServer property value must be set to a valid SMTP server.

In both of the Application_Error examples above, the request is still redirected to an error page provided by ASP.NET, or a custom error page provided by us. The error page behavior is configurable through the ASP.NET XML configuration system using web.config for the application or machine.config to configure the behavior for the entire server.

Working with Custom Errors

The default settings we find when we view machine.config are:

<customErrors mode="RemoteOnly"/>

The mode attribute has three possible settings:

  • RemoteOnly: When an unhandled exception occurs, local users will see the ASP.NET rich error page, which includes compilation details and accessibility to the full source. However, remote users are shown a different ASP.NET error page that simply lets them know an error occurred. No details are provided. If a custom error page is available, this is shown to remote users.
  • On: The detailed ASP.NET error page is never shown, even to local users. If a custom error page is available, it is always used.
  • Off: The detailed ASP.NET error page is always displayed, even if a custom error page exists.

In addition to the mode settings, there are several other configuration options for the customErrors section of the configuration.

  • defaultRedirect: A custom error page that the client is redirected to when an error occurs. For example, if we create a custom error page named customError.aspx, we can add the following configuration entry in web.config for an ASP.NET application:

    <customErrors mode="On" defaultRedirect="customError.aspx"/>
    

    Whenever an unhandled exception occurs within our application, the user is redirected to customError.aspx, which provides a user-friendly error message.

  • Special Case: This option provides a special case for the custom error page depending upon the HTTP status code. For example, we may want a custom error page if the request attempts to access a page that doesn't exist (HTTP 404). Here we can make use of another configuration element that can be nested in <customErrors>, <error> :

    <customErrors mode="On" defaultRedirect="customError.aspx">
      <error statusCode="404" redirect="custom404.aspx"/>
    </customErrors>
    

    Now, whenever a request is made for a document that cannot be located, we can send the request to a custom page. Web sites such as https://www.microsoft.com take advantage of this feature for a smart 404. For example, in custom404.aspx, they examine the originally requested URL, (that is, https://www.microsoft.com/ie5), and look for known keywords, such as ie. The custom404.aspx page can then do a look-up in a database or memory resident hash table and provide the user with a list of possible options. For example, a URL like https://www.microsoft.com/ie is likely to be returned.

Conclusion

ASP.NET makes dealing with errors easy. We can take advantage of either Page_Error or Application_Error to run code that can log the exception, and we can further customize the error page to which the user is redirected.

Next month, we'll discuss a SOAP extension used to encrypt information within the body of a SOAP message. You can view the sample that we'll discuss at https://www.gotdotnet.com/team/rhoward/ (look for the SOAP encryption sample).

Rob Howard is a program manager for ASP.NET on the .NET Frameworks team. He spends whatever spare time he has either with his family or fly fishing in Eastern Washington.