Thu
Jan 11
2007

Catching Unhandled ASP.Net Exceptions

Exceptions that occur off the worker process thread are silently swallowed by ASP.NET 1.1. Use the method described in MS article 911816 to create an HttpModule to catch the exceptions. The follow is adapted for ASP.NET 1.1 without requiring registration in the GAC and a strong name. It also logs to the EventLog as well as sending an email as

Add the following code to UnhandledExceptionModule.cs:

using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Web;
 
namespace WebMonitor {
    public class UnhandledExceptionModule: IHttpModule {
 
        static int _unhandledExceptionCount = 0;
 
        static string _sourceName = null;
        static object _initLock = new object();
        static bool _initialized = false;
        static string _mailServer = "mymailserver";
        static string _mailTo = "abc@xyz.com";
 
        public void Init(HttpApplication app) {
 
            // Do this one time for each AppDomain.
            if (!_initialized) {
                lock (_initLock) {
                    if (!_initialized) { 
 
                        _sourceName = string.Format(CultureInfo.InvariantCulture, "ASP.NET {0}.{1}.{2}.0",
							System.Environment.Version.Major, 
							System.Environment.Version.Minor,
							System.Environment.Version.Build);
 
                        if (!EventLog.SourceExists(_sourceName)) {
                            throw new Exception(String.Format(CultureInfo.InvariantCulture,
                                                              "There is no EventLog source named '{0}'.", 
                                                              _sourceName));
                        }
 
                        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledException);
 
                        _initialized = true;
 
						LogMessage("UnhandledExceptionModule started", "", EventLogEntryType.Information);
 
                    }
                }
            }
        }
 
        public void Dispose() {
        }
 
        void OnUnhandledException(object o, UnhandledExceptionEventArgs e) {
            // Let this occur one time for each AppDomain.
            if (Interlocked.Exchange(ref _unhandledExceptionCount, 1) != 0)
                return;
 
            StringBuilder message = new StringBuilder("\r\n\r\nUnhandledException logged by UnhandledExceptionModule.dll:\r\n\r\nappId=");
 
            string appId = (string) AppDomain.CurrentDomain.GetData(".appId");
            if (appId != null) {
                message.Append(appId);
            }
 
            Exception currentException = null;
            for (currentException = (Exception)e.ExceptionObject; currentException != null; currentException = currentException.InnerException) {
                message.AppendFormat("\r\n\r\ntype={0}\r\n\r\nmessage={1}\r\n\r\nstack=\r\n{2}\r\n\r\n",
                                     currentException.GetType().FullName, 
                                     currentException.Message,
                                     currentException.StackTrace);
            }           
 
			LogMessage("UnhandledExceptionModule exception", message.ToString(), EventLogEntryType.Error);
        }
 
		void LogMessage(string subject, string message, EventLogEntryType severity)
		{
			try
			{
				EventLog Log = new EventLog();
				Log.Source = _sourceName;
				Log.WriteEntry(subject + "\n" + message, severity);
			}
			catch  {}
			try
			{
				System.Web.Mail.MailMessage msg = new System.Web.Mail.MailMessage();
				msg.Body = message;
				msg.To = _mailTo;
				msg.Subject = severity.ToString() + ": " + subject;
				msg.From = _mailTo;
 
				System.Web.Mail.SmtpMail.SmtpServer = _mailServer;
				System.Web.Mail.SmtpMail.Send(msg);
			}
			catch {}
		}
 
    }
}

Open a Visual Studio Command Prompt and run: csc /t:library /r:system.web.dll,system.dll UnhandledExceptionModule.cs

Place the DLL in your bin folder

Add following to your web.config:

<system.web>
  <httpModules>
    <add type="WebMonitor.UnhandledExceptionModule, UnhandledExceptionModule" name="UnhandledExceptionModule"/>
  </httpModules>
</system.web>

Here is sample code to trigger an unhandled exception

using System.Threading;
Timer t;
private void btnTest_Click(object sender, System.EventArgs e)
{
  t = new Timer(new TimerCallback(t_Elapsed), null, 250, Timeout.Infinite);
}
private void t_Elapsed(object state)
{
  throw new Exception("My unhandled exception");
}

Leave a reply

© 2009 Brian Low. All rights reserved.