ELMAH, short for Error Logging Modules and Handlers, is an error logging solution that can be easily added into ASP.NET applications in order to trap and report on un-handled exceptions. It's a fantastic solution that all ASP.NET web developers should know about, and this article will cover some easy ways to extend it so that it can be used for more than just unhandled exceptions.
ELMAH
I won't go into too much praise of ELMAH in this article, because others have done so elsewhere. What I love most about it though, of course, is that it tracks all exceptions and displays them in a great dashboard that you can actively work with and query to learn more about the kinds of problems your site is bumping in to—as shown in Figure 1.
Figure 1: ELMAH's dashboard display of errors and details, one of its best features
Suffice it to say, however, that I use ELMAH in all of my sites and wouldn't dream of deploying them without it—as it's such a powerful way of making sure that you're keeping tabs on all of those potential un-thought-of problems that can occur with a site once it's up and running.
As Scott Hanselman outlines, the easiest way to install ELMAH is to use Nuget to drop it into a site. (To do this, just make sure you've upgraded NuGet on your workstation, and then you'll want to install the ELMAH 1.2 package (not the ELMAH Core Library that doesn't have any configuration options) to get in-memory storage and reporting. Then, if you want the ability to persist trapped errors, you'll want to install an ELMAH on SQL Server or ELMAH on XML or whatever package as an additional, separate, addition.)
Otherwise, when it comes to dropping ELMAH into MVC applications I also always go into my Global.asax and add a new route exclusion (typically right under/around the existing .axd exclusion):
routes.IgnoreRoute("favicon.ico");
This exclusion, in turn, just tells my application to ignore requests to favicon.ico—instead of trying to find a controller for it. Likewise, if you don't have a favicon.ico living in the root of your site, you'll want to add one in—otherwise once you add ELMAH you'll start getting reports of unhandled FileNotFound exceptions.
The key thing to note about these potential exceptions or errors that you'll bump into when dealing with favicon.ico is that they're NOT something caused by ELMAH. Instead, they're a perfect example of how a site that seems to be running just fine was actually throwing unhandled exceptions—which ELMAH exposes once it's deployed.
ELMAH and Handled Exceptions in MVC Applications
As phenomenal as ELMAH is at trapping unhandled exceptions, I like to make a couple of changes to my MVC applications that allow them to log certain kinds of handled exceptions into ELMAH as well. And the reason for this is that ELMAH works so insanely well as a reporting mechanism for exceptions encountered on the site that it only makes sense (to me) to use it for certain, ugly, exceptions that I may have anticipated and gracefully handled—but still wish to know about.
For example, assume that you have an MVC application where you're requiring payment as part of the signup process. In such an application you'd likely have code to trap or handle exceptions that occurred when communicating with the payment gateway or when trying to persist the results of a successful signup to the database and so on. And while it would be a best practice to make sure that those exceptions were trapped and gracefully handled (to provide for a better user experience AND to avoid information disclosure), simply swallowing these exceptions after handling them could be very costly. Whereas gracefully handling them and then routing them into ELMAH means that they'll be reported and you can even optionally set up alerts via email to be notified if/when the occur. Such is the power of ELMAH—if you route certain types of handled exceptions into it.
To meet this need, I've taken the approach of implementing a very simple interface in my MVC applications. (More specifically, I define this interface down at the level of my 'core' libraries so that I can access this interface from within Repositories, Services, and other assemblies that my MVC site might depend upon.)
As you can see, this interface is insanely simple:
public interface IErrorLogger
{
void LogException(Exception ex);
}
Then, in terms of implementation, I typically have my MVC site implement an ElmahLogger class (that I typically drop into my \Services\Logging\ folder) that looks something like this:
public class ElmahLogger : IErrorLogger
{
public void LogException(Exception ex)
{
try
{
var context = HttpContext.Current;
if (context == null)
return;
var signal = ErrorSignal.FromContext(context);
if (signal == null)
return;
signal.Raise(ex);
}
catch (Exception)
{
// swallow or handle with something else...
}
}
}