You can subclass HandleErrorAttribute
and override its OnException
member (no need to copy) so that it logs the exception with ELMAH and only if the base implementation handles it. The minimal amount of code you need is as follows:
namespace MvcDemo
{
using System;
using System.Web;
using System.Web.Mvc;
using Elmah;
public class HandleErrorAttribute : System.Web.Mvc.HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
base.OnException(context);
if (context.ExceptionHandled)
RaiseErrorSignal(context.Exception);
}
private static void RaiseErrorSignal(Exception e)
{
var context = HttpContext.Current;
ErrorSignal.FromContext(context).Raise(e, context);
}
}
}
The base implementation is invoked first, giving it a chance to mark the exception as being handled. Only then is the exception signaled. The above code is simple and may cause issues if used in an environment (such as testing) where the HttpContext
may not be available. As a result, you want to code that is more defensive at the cost of being slightly longer:
namespace MvcDemo
{
using System;
using System.Web;
using System.Web.Mvc;
using Elmah;
public class HandleErrorAttribute : System.Web.Mvc.HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
base.OnException(context);
var e = context.Exception;
if (!context.ExceptionHandled // if unhandled, will be logged anyhow
|| RaiseErrorSignal(e) // prefer signaling, if possible
|| IsFiltered(context)) // filtered?
return;
LogException(e);
}
private static bool RaiseErrorSignal(Exception e)
{
var context = HttpContext.Current;
if (context == null)
return false;
var signal = ErrorSignal.FromContext(context);
if (signal == null)
return false;
signal.Raise(e, context);
return true;
}
private static bool IsFiltered(ExceptionContext context)
{
var config = context.HttpContext.GetSection("elmah/errorFilter")
as ErrorFilterConfiguration;
if (config == null)
return false;
var testContext = new ErrorFilterModule.AssertionHelperContext(
context.Exception, HttpContext.Current);
return config.Assertion.Test(testContext);
}
private static void LogException(Exception e)
{
var context = HttpContext.Current;
ErrorLog.GetDefault(context).Log(new Error(e, context));
}
}
}
This second version will try to use error signaling from ELMAH first (which involves the fully configured pipeline like logging, mailing, filtering and what have you). Failing that, it attempts to see if the error should be filtered or not. If not, the error is simply logged. Note that this implementation does not handle mail notifications. If the exception can be signaled then a mail will be sent if configured to do so. Also bear in mind that you may have to take care that if multiple HandleErrorAttribute
instances are in effect then duplicate logging does not occur, but above two examples should get your started.