views:

11881

answers:

3

I am trying to use ELMAH to log errors in my ASP.NET MVC application, however when I use the [HandleError] attribute on my controllers ELMAH doesn't log any errors when they occur.

As I am guessing its because ELMAH only logs unhandled errors and the [HandleError] attribute is handling the error so thus no need to log it.

How do I modify or how would I go about modifying the attribute so ELMAH can know that there was an error and log it..

Edit: Let me make sure everyone understands, I know I can modify the attribute thats not the question I'm asking... ELMAH gets bypassed when using the handleerror attribute meaning it won't see that there was an error because it was handled already by the attribute... What I am asking is there a way to make ELMAH see the error and log it even though the attribute handled it...I searched around and don't see any methods to call to force it to log the error....

+136  A: 

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.

Atif Aziz
Super worked like a charm thanks.
dswatik
I can't get the ElmahHandleErrorAttribute to workMy present configuration works fine using elmah in modules and handlers I have removed the [HandleError] from my controllersI want to try the ElmahHandleErrorAttribute I have set the ElmahHandleErrorAttributeon my ApplicationController (the base class of all my controllers)but it's never called when I debug and set a break point in OnExceptionwhat am I missing ?
freddoo
Excellent. I wasn't trying to implement Elmah at all. I was just trying to hook up my own error reporting I've used for years in a way that works well with MVC. Your code gave me a starting point. +1
Steve Wortham
Thank you. I thought I was losing my mind, when really I was obverving the "HandleError is ignored if customErrors set to RemoteOnly and you're on the localhost" behavior. Turns out the code in my Application_Error event as for naught!
Nicholas Piasecki
When you have a chain of HandleErrors, like [HandleError(Order = 1, ExceptionType = typeof(NotFound), View = "Error/NotFound"), HandleError(Order = 2, View = "Error/Unexpected")] you end up with ELMAH sending you a chain of emails for a single error. Is there any way to avoid that?
J. Pablo Fernández
There seems to be a bug in the above code. The first code logs if exception is handled but the latter tries to log when the exception is not handled. I think we only want to log when it's handled by the base class.
Jiho Han
I would like to add a custom view to this solution but calling context.Result = new ViewResult() { ViewName = "Error" }; does not seem to do anything.
Myster
in regard to my previous comment, I was missing: context.ExceptionHandled = true;
Myster
Thanks! Fixed my issue in about 30 seconds.
David Lively
+2  A: 

You can take the code above and go one step further by introducing a custom controller factory that injects the HandleErrorWithElmah attribute into every controller.

For more infomation check out my blog series on logging in MVC. The first article covers getting Elmah set up and running for MVC.

There is a link to downloadable code at the end of the article. Hope that helps.

http://dotnetdarren.wordpress.com/

Darren
Seems to me like it would be a lot easier to just stick it on a base controller class!
Nathan Taylor
Darren's series above on logging and exception handling is well worth the read!!! Very thorough!
Jeff Spicoli