As developers, we can only fix things if we know they're broken. I've been a part of several companies in which the users will encounter a fatal error on a web page and just close the browser and go home. That doesn't help anybody, especially me (as the developer) because even though the application went through several rounds of testing and bug fixing, two months down the road I could find myself in a meeting with a dozen people who suddenly bring up the fact that only two thirds of the application is useful because there are a bunch of problems that I didn't fix. In this article we'll implement automatic error logging so the development team knows about virtually every fatal error that is encountered in our ASP.NET application.
The web.config file has a section called <customErrors> that we can use to redirect the user to a nice looking page that we have designed to explain to them that there has been a problem. We can even specify different pages for each type of error (such as 404 - File Not Found). For now we'll redirect to a single generic page called DefaultErrorPage.aspx. To do this, we'll alter our web.config file.
<customErrors defaultRedirect="DefaultErrorPage.aspx" mode="on">
</customErrors>
DefaultErrorPage.aspx can show whatever you want to the user, so we won't be creating one in this article. Just make sure you add one to your project.
It is quite simple to cause a fatal error on web page on purpose. For our example let's choose a SqlException (from the System.Data.SqlClient namespace). To cause an exception to be thrown we'll attempt to open a connection to a database, but we'll purposely specify a database that does not exist. Add the following code to the Page_Load routine of your web form.
VB
Dim c As SqlConnection = New SqlConnection("server=MyServer;database=IncorrectDatabase;user id=MyUser;password=MyPassword")
c.Open()
C#
SqlConnection c = new SqlConnection("server=MyServer;database=IncorrectDatabase;user id=MyUser;password=MyPassword");
c.Open();
If you want you can replace the connection string above with one of your own, but the one provided should cause an exception to be thrown anyway.
Let's decide what we believe would be useful information to trap. Obviously we'll need a URL here. More specifically, we'll need a URL with all the QueryString parameters that were passed when the page error occurred. If this is an Intranet and we were using Integrated Windows Authentication we could also grab the AUTH_USER from the ServerVariables. Let's keep it simple and decide that we'll grab the Exception and the accompanying StackTrace. This will tell us exactly what error message would have been displayed to the user had we not implemented our own custom error page. It will also show us the most recent items in the stack trace (if available) so we can see what caused the error that we'll need to fix.
The next thing we'll need to do is edit the code behind global.asax to log our error. In a good software architecture we might pass the error logging functionality off to a class library that our enterprise uses, but for the sake of this article we'll just ignore best practices and show how automatic error logging can be done.
We should find a subroutine called Application_Error in global.asax.vb. This is where our automatic error logging actually takes place. Within this routine we could add code to save the details of the error to a database or pass the details to an object in our class library that might do more with the error, but for now we'll just send an email to Joe Programmer. Joe is in charge of development for the entire site. To send email we'll need to reference the System.Web.Mail namespace in our global.asax.cs or global.asax.vb file.
VB
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
Dim msg As MailMessage = New MailMessage()
SmtpMail.SmtpServer = "MyMailServer"
msg.Body = "Error: " & Server.GetLastError().InnerException.ToString() & vbCrLf & vbCrLf & Request.Url
msg.To = "joe@..."
msg.From = "errors@..."
msg.Subject = "Application Error"
SmtpMail.Send(msg)
End Sub
C#
protected void Application_Error(Object sender, EventArgs e)
{
MailMessage msg = new MailMessage();
SmtpMail.SmtpServer = "MyMailServer";
msg.Body = "Error: " + Server.GetLastError().InnerException.ToString() + "\n\n" + Request.Url;
msg.To = "joe@...";
msg.From = "errors@...";
msg.Subject = "Application Error";
SmtpMail.Send(msg);
}
Believe it or not that's all there is to it to implement a system in which every fatal error is brought to the attention of the person (or people) who have the responsibility of reviewing it and fixing it. As I said earlier, you'll most likely want to design a database table that would hold the URL, User, Stack Trace, Exception Text, and any other information that you might find useful. This would lend itself to a bug-tracking system in which each fatal error could be accounted for from the date it was encountered to the date the fix was deployed. We might also alter the email address in the example above to a mobile device email address so the people with the power to fix problems are notified as soon as possible.