Introduction
When developing an ASP.NET application, one of the routine tasks is to plan for unhandled exceptions. If the exception is left unhandled, by default, the ASP.NET runtime will either display a nasty error message to your user or it will show the exception details page, depending on how you configure your website. Allowing the user to see a nasty error messages is far from recommended (see this article for exception handling best practices). If you simply show a generic exception details page, you will lose valuable information you may need to debug the issue.
For a few years, I have been using this strategy for processing unhandled exceptions in my websites. When an unhandled exception occurs, the strategy is to:
- Catch the error in the application-level Error event handler (Global.asax).
- "Process" the error by sending an email containing the exception details.
- Redirect to a custom webpage displaying a user-friendly message to the end user.
Now, I am not suggesting that you should not use try catch
blocks in your code to catch errors where you can recover. This is a strategy for catching those unexpected errors in one central location, logging the exception information, sending an email to a developer, and displaying a friendly message to your user.
The problem with the approach I have been using is that it leaves the logging and sending of an email up to you to manually implement. In fact, I was only sending out an email with the error message and relying on the server's application logs to record the errors. Tracking down the problem, who the user was, etc., became a real pain using only an email message and application logs. I wished for an easier way to receive a notice of an error and a way to store the information so that I could run a report to view the errors or even see some statistics.
So, I started doing some research on the Microsoft Enterprise Application blocks, and thought that they were exactly what I was looking for. This article in particular was very helpful, and you may want to follow this approach for your N-Tiered applications.
What I love about using the application blocks is that you simply enter a few settings in the web.config file, add references to the application blocks, and with a few lines of code, I was sending emails and logging exceptions to SQL Server. Next, one quick Reporting Services report later, and I could easily view the information in the Logging database!
Using the code
What you will need to use this code:
- MS Enterprise Library 5.0
- Must be using at least .NET Framework 3.5
- SQL Server - with the Enterprise Library Logging database installed
From the MS Enterprise Library documentation:
"In the folder where you installed the Enterprise Library, open the subfolder Source\Blocks\Logging\Src\DatabaseTraceListener\Scripts in Windows Explorer. This folder contains a SQL script named LoggingDatabase.sql and a command file named CreateLoggingDb.cmd that executes this script. The script creates a new database named Logging, and adds to it all of the tables and Stored Procedures required by the database trace listener to write log entries to a database."
Add references to the following application blocks (should be located at C:\Program Files (x86)\Microsoft Enterprise Library 5.0\Bin\):
- Microsoft.Practices.EnterpriseLibrary.Common.dll
- Microsoft.Practices.EnterpriseLibrary.Data.dll
- Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.dll
- Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.dll
- Microsoft.Practices.EnterpriseLibrary.Logging.dll
- Microsoft.Practices.EnterpriseLibrary.Logging.Database.dll
After you install the MS Enterprise Library, when you right click on the web.config file in Visual Studio, you will see a menu option for Edit Enterprise Library V5 Configuration. You can use this tool to enter the necessary configuration info, or you can do it by manually updating the web.config file. I strongly recommend the former especially if you are new to using the application blocks. For the purposes of this article, under the Blocks menu, choose Add Logging Settings and Add Exception Handling Settings. By default, you should already have Application Settings and Database Settings. After you have those added, you can close and save the tool, which will update your web.config file.
For simplicity, update the web.config with this code snippet. You can then go back to the tool to more easily see the settings. Make sure to update the email addresses and STMP server in the Logging Settings and the connection string for the Logging database under Database Settings.
The snippet below has been wrapped to avoid page scrolling.
<configsections />
<section name="loggingConfiguration" requirepermission="true"
type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings,
Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<section name="exceptionHandling" requirepermission="true"
type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.
Configuration.ExceptionHandlingSettings,
Microsoft.Practices.EnterpriseLibrary.ExceptionHandling,
Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
...
</configsections />
<loggingconfiguration tracingenabled="true"
defaultcategory="General" />
<listeners />
<add name="Email Trace Listener"
type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.
EmailTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging,
Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
listenerdatatype="Microsoft.Practices.EnterpriseLibrary.Logging.
Configuration.EmailTraceListenerData,
Microsoft.Practices.EnterpriseLibrary.Logging,
Version=5.0.414.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"
toaddress="someone@yourcompany.com"
fromaddress="noreply@yourcompany.com"
subjectlineender="- Log Example Website"
smtpserver="YourSMTPServer"
formatter="Email Text Formatter"
traceoutputoptions="DateTime, Timestamp, ProcessId, Callstack" />
<add name="Database Trace Listener"
type="Microsoft.Practices.EnterpriseLibrary.Logging.Database.
FormattedDatabaseTraceListener,
Microsoft.Practices.EnterpriseLibrary.Logging.Database,
Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
listenerdatatype="Microsoft.Practices.EnterpriseLibrary.Logging.
Database.Configuration.FormattedDatabaseTraceListenerData,
Microsoft.Practices.EnterpriseLibrary.Logging.Database,
Version=5.0.414.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"
formatter="Text Formatter"
traceoutputoptions="LogicalOperationStack, DateTime,
Timestamp, ProcessId, ThreadId, Callstack"
databaseinstancename="LoggingConnectionString"
writelogstoredprocname="WriteLog"
addcategorystoredprocname="AddCategory" />
</listeners />
<formatters />
<add name="Text Formatter"
type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter,
Microsoft.Practices.EnterpriseLibrary.Logging,
Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
template="Application Name: Log Example Website
Message: {message}{newline}
Category: {category}{newline}
Extended Properties: {dictionary({key} - {value}{newline})}" />
<add name="Email Text Formatter"
type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter,
Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35"
template="There was an error. Please see the Logging database
for more information. Timestamp: {timestamp(local)}
{newline} Message: {message}{newline}" />
</formatters />
<categorysources />
<add name="ErrorLogging" switchvalue="All" />
<listeners />
<add name="Email Trace Listener" />
<add name="Database Trace Listener" />
</listeners />
</add />
</categorysources />
<specialsources />
<allevents name="All Events" switchvalue="All" />
<notprocessed name="Unprocessed Category" switchvalue="All" />
<errors name="Logging Errors & Warnings" switchvalue="All" />
<listeners />
<add name="Email Trace Listener" />
<add name="Database Trace Listener" />
</listeners />
</errors />
</specialsources />
</loggingconfiguration />
<exceptionhandling />
<exceptionpolicies />
<add name="AllExceptionsPolicy" />
<exceptiontypes />
<add name="All Exceptions"
type="System.Exception, mscorlib, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089"
posthandlingaction="None" />
<exceptionhandlers />
<add title="Enterprise Library Exception Handling"
name="AllExceptionsLoggingHandler"
type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.
Logging.LoggingExceptionHandler, Microsoft.Practices.
EnterpriseLibrary.ExceptionHandling.Logging,
Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
logcategory="ErrorLogging"
eventid="100" severity="Error"
formattertype="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.
TextExceptionFormatter,
Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"
priority="0" />
</exceptionhandlers />
</add />
</exceptiontypes />
</add />
</exceptionpolicies />
</exceptionhandling />
<connectionstrings />
<add name="LoggingConnectionString"
connectionstring="Data Source=YourSQLServer;
Initial Catalog=Logging;
Integrated Security=SSPI"
providername="System.Data.SqlClient" />
</connectionstrings />
Place this code in the Global.asax file:
protected void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
if (ex is HttpUnhandledException && ex.InnerException != null)
{
ex = ex.InnerException;
}
if (ex != null)
{
Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.
ExceptionPolicy.HandleException(ex, "AllExceptionsPolicy");
Server.ClearError();
Response.Redirect("~/Utility/ErrorPage.htm");
}
}
As you can see from the code above, after the error is handled by the application block, the user will be redirected to the ~/Utility/ErrorPage.htm page. So, you will need to create a page for your site, or you can use the one in the sample code for download in this article.
So, that is all there is to using the MS Enterprise Application Blocks to process unhandled exceptions in an ASP.NET website. Download the sample code, which contains a project to demo how to process unhandled exceptions. If you are familiar with SQL Server Reporting Services, you can create your own custom reports to view the data logged to SQL Server.
In closing, this is my first article so any feedback would be greatly appreciated. I hope someone finds it helpful... Thanks!