Many ASP.NET developers think that having code in the Application_Error method in Global.asax.cs is adequate for dealing with any unhandled exceptions within their web application. While it usually is adequate--for most basic web applications, there are particular cases where this simply won't catch all errors.
Specifically, if you have code running on the server that is in a thread pool, or you've spawned a separate thread, or you have callback code for a timer, if an unhandled exception occurs, you just might be out of luck.
You can easily test this for yourself by creating a web page with two buttons, one that throws an exception when you click it, and the other that uses a thread from the thread pool and throws an exception within it. Something like this:
public partial class _Default : System.Web.UI.Page
{
private void ThrowException()
{
throw new ApplicationException("Ouch!");
}
protected void Button1_Click(object sender, EventArgs e)
{
ThrowException();
}
protected void Button2_Click(object sender, EventArgs e)
{
System.Threading.ThreadPool.QueueUserWorkItem( new System.Threading.WaitCallback(DoWork));
}
private void DoWork(object dummy)
{
ThrowException();
}
}
In addition to the HttpUnhandledException that gets sent to the Application_Error method, you need to handle the UnhandledException event on the AppDomain.
You can set up a handler in the Application_Start method in Global.asax.cs. But if you go a step further, you can create something a little more reusable. Here's an HttpModule that will demonstrate handling both kinds of web exeptions--those occurring during a normal web request, and those occurring on separate threads. Perhaps it should be part of the Logging Framework.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using BitFactory.Logging;
namespace WebAppA
{
public class MyExceptionModule : IHttpModule
{
private HttpServerUtility Server { get; set; }
#region IHttpModule Members
public void Dispose()
{
}
public void Init(HttpApplication context)
{
// deal with any non-main thread exceptions
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
// deal with any HttpUnhandledExceptions
context.Error += new EventHandler(context_Error);
Server = context.Server;
}
void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
// If we are here, the process is about to die
ConfigLogger.Instance.LogFatal(((Exception)e.ExceptionObject).Message);
}
void context_Error(object sender, EventArgs e)
{
// The outer exception is usually an HttpUnhandledException, so use the InnerException if it exists.
// Even more detail can be retrieved by iterateing over each InnerException, if desired.
Exception ex = Server.GetLastError();
ex = ex.InnerException ?? ex;
ConfigLogger.Instance.LogError(ex.Message);
}
#endregion
}
}
Modify the above to suit your needs. In addition, the following would go into your web.config file.
<httpModules> <add name="MyExceptionModule" type="WebAppA.MyExceptionModule, WebAppA" /> </httpModules>
You might consider putting something like this into a separate utility assembly that you can reuse in all your web projects.
2 comments: