:::: MENU ::::

Friday, June 6, 2008

Exception handling plays an important part of application management and user experience. If implemented correctly it can make maintenance easier and bring the user experience to a higher level. If not, it can be a disaster.

How many times have you seen the error message that doesn't make any sense or at least provides some valuable information, or even better - how many times have you seen the famous error screen with exception message and a complete stack trace on yellow background? Too many times, I would say. This is why, among other things, some of my colleagues were very interested in exception handling techniques and best practices.

The goal of this article is to provide an overview of what exception handling is from the user perspective and the perspective of people who maintain the application, and to show the best practices of how to implement useful error handling in ASP.NET web applications. This article is related to my previous articles CSS Message Boxes for different message types and Create MessageBox user control using ASP.NET and CSS since this two articles describes how to show user-friendly messages.

1. What information should be presented to the user?

Like I mentioned before, meaningless error messages will confuse users. Not having any error message and allowing the application to stop will make them wish they never clicked on the link that pointed to your website. :)  Messages like "An error occurred" or "System.InvalidOperationException: The ConnectionString property has not been initialized" mean nothing to the end user. One doesn't know what has happened exactly, has the information been saved, and what should one do next.

The goal of useful exception handling is to enable users to understand what has gone wrong and to continue using your application even if an error occurred.

So, the least you have to do is show a simple error page to the user. It has to show very basic information of what happened and what the user can do next. For example, you can show the general error message and a back button that can send the user back to the previous page.

This is the minimum of what you can do. Furthermore, you can provide a user with a meaningful set of messages that will explain:

  • what happened
  • what will be affected
  • what the user can do from there
  • and any valuable support information

By doing this you are eliminating the confusion in users and allowing them to react properly. Image below shows an example of a well designed error screen.

Now let's see what else is needed.

2. Logging exceptions

In order to enable successful application maintenance you have to log exceptions. The most important thing for people who maintain web applications is the exception log.  This log stores detailed information on problems that have happened.and provides developers with useful information for correcting those problems

What information should be stored in exception log?

First of all, you have to save as much information as you can get from the exception. That means date and time, exception message, exception type and stack trace.

You could, however, log more information, for example authenticated or anonymous user information. Exception Management Architecture Guide on MSDN gives the list of possible information that can be logged:

Data

Source

Date and time of exception

DateTime.Now

Machine name

Environment.MachineName

Exception source

Exception.Source

Exception type

Type.FullName obtained from Object.GetType

Exception message

Exception.Message

Exception stack trace

Exception.StackTrace—this trace starts at the point the exception is thrown and is populated as it propagates up the call stack.

Call stack

Environment.StackTrace—the complete call stack.

Application domain name

AppDomain.FriendlyName

Assembly name

AssemblyName.FullName, in the System.Reflection namespace

Assembly version

Included in the AssemblyName.FullName

Thread ID

AppDomain.GetCurrentThreadId

Thread user

Thread.CurrentPrincipal in the System.Threading namespace

The more information you log the easier will be for developers to determine the cause of the error and to correct it.

Where should exception log be stored?

You can log exceptions wherever you want: text file, XML file or database. I always use the database, because it allows me to track log easily and to flag exceptions that are resolved. If exception can't be stored in the database, due to a connection problem, XML file can be used.

The easiest way to do this is to have ExceptionLog table in the database and to create your own exception log provider.

This provider can be a single class that will have three methods: LogException - that will parse the exception and save all the information in the table, ResolveException - that will remove the exception from the log and GetExceptions that will return the list of unresolved exceptions.

This is an easy and simple way to trace and correct errors in a live application.

3. How to implement a useful exception handling?

First, you should consider how the exceptions will be caught. If you are building an n-tier application, you will have to catch the exceptions in middle tier classes, wrap them and throw new exception with the useful information for the client.

To send useful information to the client (such as what the user can do next) you will have to create a custom exception class that inherits from ApplicationException class and add some properties. If you want to send those four bullets I described earlier, you will have to add four more properties to your class. You will also have to override the ApplicationException constructors, and GetObjectData method in order to enable serialization. This is an example of a custom exception class called MyException.

public class MyException : ApplicationException
{
 
    #region Constructors
    public MyException(string message)
        : base(message)
    {
    }
 
    public MyException(string message, Exception inner)
        : base(message, inner)
    {
    }
 
    public MyException(string message, Exception inner,
        string _whatHappened, string _whatHasBeenAffected,
        string _whatActionsCanUserDo, string _supportInformation)
        : base(message, inner)
    {
        whatHappened = _whatHappened;
        whatHasBeenAffected = _whatHasBeenAffected;
        whatActionsCanUserDo = _whatActionsCanUserDo;
        supportInformation = _supportInformation;
    }
 
    protected MyException(SerializationInfo info,
        StreamingContext context)
        : base(info, context)
    {
        whatHappened = info.GetString("whatHappened");
        whatHasBeenAffected = info.GetString("whatHasBeenAffected");
        whatActionsCanUserDo = info.GetString("whatActionsCanUserDo");
        supportInformation = info.GetString("supportInformation");
    }
    
Categories: