Logging ASP.NET Application Shutdown Events
Someone on a listserv recently asked whether there was a way to figure out why and when ASP.NET restarts application domains. Specifically, he was looking for the exact cause of what was triggering them on his application in a production shared hosted environment (was it a web.config file change, a global.asax change, an app_code directory change, a directory delete change, max-num-compilations reached quota, \bin directory change, etc).
Thomas on my team has a cool code-snippet that he wrote that uses some nifty private reflection tricks to capture and log this information. It is pretty easy to re-use and add into any application, and can be used to log the information anywhere you want (the below code use the NT Event Log to save it – but you could just as easily send it to a database or via an email to an admin). The code works with both ASP.NET V1.1 and ASP.NET V2.0.
Simply add the System.Reflection and System.Diagnostics namespaces to your Global.asax class/file, and then add the Application_End event with this code (note: you can also download a .zip file containing the code from here):
public void Application_End() {
HttpRuntime runtime = (HttpRuntime) typeof(System.Web.HttpRuntime).InvokeMember("_theRuntime",
BindingFlags.NonPublic
| BindingFlags.Static
| BindingFlags.GetField,
null,
null,
null);
if (runtime == null)
return;
string shutDownMessage = (string) runtime.GetType().InvokeMember("_shutDownMessage",
BindingFlags.NonPublic
| BindingFlags.Instance
| BindingFlags.GetField,
null,
runtime,
null);
string shutDownStack = (string) runtime.GetType().InvokeMember("_shutDownStack",
BindingFlags.NonPublic
| BindingFlags.Instance
| BindingFlags.GetField,
null,
runtime,
null);
if (!EventLog.SourceExists(".NET Runtime")) {
EventLog.CreateEventSource(".NET Runtime", "Application");
}
EventLog log = new EventLog();
log.Source = ".NET Runtime";
log.WriteEntry(String.Format("\r\n\r\n_shutDownMessage={0}\r\n\r\n_shutDownStack={1}",
shutDownMessage,
shutDownStack),
EventLogEntryType.Error);
}
I tried this out using a simple web-site using ASP.NET 2.0 and the built-in VS Web-Server (aka Cassini). When I changed the web.config file in my running application, the following was logged to my "Application" event viewer:
_shutDownMessage=CONFIG change
HostingEnvironment caused shutdown
CONFIG change
CONFIG change
_shutDownStack= at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
at System.Environment.get_StackTrace()
at System.Web.HttpRuntime.ShutdownAppDomain()
at System.Web.Hosting.HostingEnvironment.ShutdownThisAppDomainOnce()
at System.Web.Hosting.HostingEnvironment.InitiateShutdownWorkItemCallback(Object state)
at System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object state).
When I updated my Global.asax file with some code change, the following was logged:
_shutDownMessage=Change in GLOBAL.ASAX
Change in GLOBAL.ASAX
Change in GLOBAL.ASAX
HostingEnvironment caused shutdown
_shutDownStack= at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
at System.Environment.get_StackTrace()
at System.Web.HttpRuntime.ShutdownAppDomain()
at System.Web.HttpApplicationFactory.OnAppFileChange(Object sender, FileChangeEvent e)
at System.Web.DirectoryMonitor.FireNotifications()
at System.Web.Util.WorkItem.CallCallbackWithAssert(WorkItemCallback callback)
at System.Web.Util.WorkItem.OnQueueUserWorkItemCompletion(Object state)
at System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object state).
And when I changed the contents of my \bin directory I got:
_shutDownMessage=Change Notification for critical directories.
bin dir change or directory rename
HostingEnvironment caused shutdown
Directory rename change notification for 'E:\Unload'.
Unload dir change or directory rename
_shutDownStack= at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
at System.Environment.get_StackTrace()
at System.Web.HttpRuntime.ShutdownAppDomain()
at System.Web.Hosting.HostingEnvironment.ShutdownThisAppDomainOnce()
at System.Web.Hosting.HostingEnvironment.InitiateShutdownWorkItemCallback(Object state)
at System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.
Hopefully this is a useful trick that you can re-use in your own applications to get better visibility into what is going on with them. If you are using ASP.NET 2.0, then you should definitely also investigate the new ASP.NET 2.0 Health Monitoring feature-set. This provides a rich eventing architecture for instrumenting your code, as well as raising notifications of issues to admins when they occur within your application. K. Scott Allen has written a good overview article of how this new feature-set works, and links to the MSDN documentation.
Hope this helps,
Scott