Daniel M. Hendricks

E-mailing Unhandled Exceptions in ASP.NET

Background

y feelings for ASP.NET are really part of a love-hate relationship. I like the .NET platform and how it works. But there are some shortcomings in its design that can be really annoying at times.

Being the primary developer for an Intranet, I wanted to be notified when a user experienced an unhandled exception when viewing one of my pages. This has been fairly easy to do in ASP by using exception handlers in global.asax, but proved to be much more difficult in ASP.NET.

I browsed countless articles and threads on reporting and logging exceptions using global.asax and pipelining modules in .NET, but they all came up short. They described very well how to implement such solutions, but I still wasn’t getting the information I really wanted. Most suggested using Server.GetLastError() to retrieve the last exception, but that always produce the infamous “External component has thrown an exception” message. Pretty useless, considering the user is shown a more specific error message which includes the line numbers.

My solution below is sort of a hack, but it works for me. It simply e-mails the HTML error message that the user sees. The key to this code is casting Server.GetLastError().GetBaseException() as an HttpCompileException and using the GetHtmlErrorMessage() method to retrieve the full error displayed to the user. If you have specific suggestions for extracting this information, including line numbers, in a more efficient manner, please let me know.

Summary

Because I’ve had problems with using global.asax, including performance issues and strange errors with third-party components, I decided to use an HttpModule instead. Although this required the extra step of compilation (if you know me, I code my scripts using inline code with TextPad), it has proven to be a reliable solution for e-mailing unhandled exceptions.

The Code

using System;
using System.Web;
using System.Web.Mail;

namespace Hendricks.ErrorHandler
{
public class ErrorModule : IHttpModule
{
public void Init (HttpApplication app)
{
app.Error += new System.EventHandler (OnError);
}

public void OnError (object sender, EventArgs args)
{
HttpContext ctx = HttpContext.Current;
HttpCompileException ex = (HttpCompileException)
ctx.Server.GetLastError().GetBaseException();
string txtError = ex.GetHtmlErrorMessage().ToString();

MailMessage msg = new MailMessage();

msg.To = "admin@example.com";
msg.From = "admin@example.com";
msg.Subject = "Unhandled Exception: " + ctx.Request.ServerVariables["SCRIPT_NAME"];
msg.BodyFormat = MailFormat.Html;
msg.Body = txtError;

SmtpMail.SmtpServer = "localhost";
SmtpMail.Send(msg);

// UNCOMMENT THE NEXT TWO LINES FOR A CUSTOM MESSAGE
//ctx.Response.Write("An unhandled exception had occurred. Sorry about that.");
//ctx.Server.ClearError();

// UNCOMMENT THE NEXT LINE TO TRANSFER TO A DIFFERENT PAGE
//Server.Transfer("/ErrorPage.aspx");

}

public void Dispose () {}
}
}

Installation

  1. Place the above code in a text file (ex: Hendricks.ErrorHandler.cs)
  2. Compile the code:
    csc /t:library /out:Hendricks.ErrorHandler.dll /r:System.Web.dll /r:System.dll Hendricks.ErrorHandler.cs
  3. Place the compile DLL (Hendricks.ErrorHandler.dll) in your /bin folder.
  4. Add the following lines to your Web.Config file:
<httpModules>
   <add type="Hendricks.ErrorHandler.ErrorModule,Hendricks.ErrorHandler" name="ErrorModule" />
</httpModules>

That’s about it. You can now test a page with an unhandled exception and see if the results get e-mailed to you. You don’t need to restart IIS to make this work.

Improvements

The code above is a very simple example to get you started. My production version includes such features as:

  • Handling of exceptions that may occur in the exception handler.
  • If the user is an administrator (as determined by their login username), display the message. For all other users (including non-logged in users, show a friendly error page and send an e-mail to the administrator).
  • Add some other debugging information to the e-mail like browser type (HTTP_USER_AGENT), logged-in username (if available), and IP address.

You might also consider changing the code to store the exceptions in a database, and sending the user to a specific error page depending on the result of Server.GetBaseException().GetHttpCode(). I will leave it up to you to make these improvements to suit your needs.

Post a Comment

You must be logged in to post a comment.

Tip: Sign up for a free Gravatar to have a photo next to your comment! Your gravatar will follow you around when you post to blogs that support it, based on the e-mail address you use to post.