Fri
Aug 3
2007

Global exception handling

Some general notes on exception handling:

  • Setup a global exception handler to catch and handle unexpected exceptions.
  • Only catch exceptions where you can take more intelligent action than the global exception handler.
  • Be careful with exceptions on any thread other than the main thread (spawned threads, asynchronous delegates, timers, etc…)
    • In .NET 1.1, the CLR eats any exceptions, killing the thread but allowing your app to continue.
    • In .NET 2.0, unhandled exceptions take down the entire appdomain (more info).

In ASP.NET
1. Add the following to global.asax:

void Application_Error(object sender, EventArgs e) 
{
    Exception ex = Server.GetLastError();
    if (ex is System.Web.HttpUnhandledException)
    {
       ex = ex.InnerException;
    }
    LogHelper.Log.Error("Unhandled exception", ex);
    Server.Transfer("~/Error.aspx");
}

2. Create an Error.aspx page:
Use Server.GetLastError() to retrieve the exception.
Ensure the exception is logged in global.asax in case the Server.Transfer() fails.
Do not display the exception to the user to prevent hackers from gleaning information.

3. If you are using Forms Authentication, add the following to your web.config. It ensures unauthenticated users can see the Error.aspx page in case an error occurs during logon/logoff. You should do the same for any images and stylesheets as well.

<configuration>
	<location path="Error.aspx">
		<system.web>
			<authorization>
				<allow users="?"/>
			</authorization>
		</system.web>
	</location>
</configuration>

4. If you are using AJAX, you have to add an event handler on every page (are there any better solutions?):

protected void ScriptManager1_AsyncPostBackError(object sender, AsyncPostBackErrorEventArgs e)
{
    LogHelper.Log.Error("Unhandled exception during asyncpostback", e.Exception);
    ScriptManager1.AsyncPostBackErrorMessage = 
        "An unexpected error occured. Please try again or contact the Help Desk.";
}

ASP.NET Web Services
The Global.Application_Error does not fire in a web service.

using System;
using System.Web.Services.Protocols;
using TDR.Common.Utility;
 
namespace MySolution.MyProject.SoapExtensions
{
    public class LoggingSoapExtension : SoapExtension
    {
        public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
        {
            return null;
        }
 
        public override object GetInitializer(Type serviceType)
        {
            return null;
        }
 
        public override void Initialize(object initializer)
        {
        }
 
        public override void ProcessMessage(SoapMessage message)
        {
            if (message.Stage == SoapMessageStage.AfterSerialize)
            {
                if (message.Exception != null)
                {
                    LogManager.Log.Error(message.Exception.Message, message.Exception);
                }
            }
        }
    }
}

2. Add this to your web.config:

<system.web>
    <webServices>
        <soapExtensionTypes>
            <add type="MySolution.MyProject.SoapExtensions.LoggingSoapExtension,MySolution" priority="1" group="1" />
        </soapExtensionTypes>
    </webServices>
</system.web>

Note: SoapExtensions are not called when invoking the a webservice from the built-in test webpage.

WinForms, Console Apps and Services
1. Hook into the domain’s unhandled exception event:

static void Main()
{
    AppDomain.CurrentDomain.UnhandledException +=
        new UnhandledExceptionEventHandler(
            CurrentDomain_UnhandledException);
}
 
/// <summary>
/// Log unhandled exceptions that occur anywhere in the appdomain.
/// </summary>
static void CurrentDomain_UnhandledException(object sender, 
                                     UnhandledExceptionEventArgs e)
{
    try 
    {
 
        LogHelper.Log.Error("Unhandled exception.", 
                   e.ExceptionObject as Exception); 
    }
    catch { }
}

Leave a reply

© 2006 Brian Low. All rights reserved.