Cures for the Common Ailments - Part 5: Reporting Exceptions through E-Mail
A few months ago I made an entry reviewing how to send e-mail from an ASP.NET page, today I am going to extend that entry a little an show how you can make a simple exception reporting utility for your web pages.
One of the terrible things we as developers have to deal with is unhandled exceptions, we all have them good and bad developers. The problem is that no matter how well thought out an application may be we will always have holes that we never anticipated. Unfortunately one day an end user will find it and our application will ungracefully blow up.
The first thing I want to know is what are the details of error and if possible how did the user invoke this horrible issue. Because let's face it, it worked on your machine didn't it. I need to know as much as possible so I can properly assess the situation, properly debug the code and apply a reasonable solution to my code. Typically when I interview a user that caused the exception, if I actually have a user to talk to, they generally say something like 'I just clicked the button'. That is useless and we need to control the situation more. Years ago I stopped relying on end users all together to tell me anything about an exception, and started adding some code to my applications to send me exception notices with as much as I can possible gather from the application itself.
Today I generally use the Enterprise Library Logging Application block to track these exception reports for me, but I am not going to cover that here. I will leave that to a future entry. Today I am going to offer a generic method for managing these reports.
First is to create a base page class, again this is the typical architecture I use, a base page class derived from System.Web.UI.Page to contain common methods and event handlers I use over and over. I call my base page class ExBasePage, but you can call yours anything you wish.
The ASP.NET pipeline consists of several events along the way, at the application level and the page level. Each of these has an Error event which is fired when any unhandled exception is bubbled to it. You can catch this event in controls and Master Pages as well. Today I will focus on creating a simple exception reporting routine that will e-mail the contents of an exception from the Page_Error event handlers.
When an exception is thrown a detailed message, or at least we hope enough of a message, is created to give us a decent idea as to what happened and if you have the application in debug mode where it happened. This is the Exception class or any of its derived types. The base Exception class has seven properties of interest, in particular Message and Source.
The Message property contains a message that is supposed to explain the current exception. Sometimes this is just not detailed enough to help. If your application, web or desktop, is still compiled in debug mode or you just happen to have the Object libraries included you will actually get some information to pinpoint the error with the StackTrace.
The StackTrace property gives us a list of all the steps that were executed to get to the point of our error. So this property will essentially give us a list of methods that were called in the order they were called so we might be able to see how we got to the exception point.
In order to enable our page to e-mail us exception reports we need to make sure we have the SMTP server and destination addresses configured on the site.
It is of course never good to hard code these values, so you should put them in some sort of site configuration like the web.config. As far as the destination address you should probably set up a distribution list or alias that you can change in the e-mail server. This is a good idea because you or the current person you are assigning this responsibility to may not be there in the future.
The Page_Error event handler passes two arguments, that we will not need. Instead we will call Server.GetLastError().GetBaseException() to get the details of our error. It is also a good idea to cast this to a local Exception object.
VB:
dim objErr as Exception= Server.GetLastError().GetBaseException()
C#:
Exception objErr = Server.GetLastError().GetBaseException();
Once you have a local copy of the Exception you can create a useful message to report the error to you. Typically I would create a report template and either save it at a local template file or in the site database. I create these templates to have merge fields, generally delimited with ## on each side of a field name. For example ##Message## would be the area I would merge in the actual Exception message in the report. You can easily perform a merge on a string using the String.Replace method.
In the exception report you should pass as much information as you possibly can, this includes the time, URL, Client IP address, browser, etc. All this will be useful in tracking down the source of the bug.
Once you have the detailed error message just e-mail to yourself, or maybe a generic error handling inbox for a group of developers to review and correct the issue.
Public Sub SendMsg(ByVal sMsg As String, _
ByVal sTo As String, ByVal sFrom As String, _
ByVal sSubject As String)
If IsValidEmail(sFrom) = False Then
Exit Sub
End If
Dim oMailMsg As New System.Net.Mail.MailMessage()
'Split the ToAddress in case of multiple destinations
For Each sToAddr As String In sTo.Split(',')
oMailMsg.To.Add(New MailAddress(sToAddr))
Next
oMailMsg.From = New MailAddress(sFrom)
oMailMsg.Subject = sSubject
oMailMsg.Body = sMsg
oMailMsg.IsBodyHtml = True
'Should be able to get the SMTP from the Web.config setup, but I show generic method here for that purpose.
Dim client As New SmtpClient(GetSMTP())
client.Send(oMailMsg)
End Sub
In the near future look for an entry on reporting exceptions via an httpModule.
Resources: Custom Error Reporting in ASP.NET