Как заставить ELMAH работать с атрибутом ASP.NET MVC [HandleError]?

Я пытаюсь использовать ELMAH для регистрации ошибок в моем приложении ASP.NET MVC, однако, когда я использую атрибут [HandleError] на моих контроллерах, ELMAH не регистрирует никаких ошибок, когда они происходят.

Как я предполагаю, потому что ELMAH регистрирует только необработанные ошибки, а атрибут [HandleError] обрабатывает ошибку, поэтому нет необходимости регистрировать ее.

Как мне изменить или как я могу изменить атрибут, поэтому ELMAH может знать, что произошла ошибка и зарегистрировать его.

Изменить: Позвольте мне убедиться, что все понимают, я знаю, что могу изменить атрибут, который не является вопросом, который я задаю... ELMAH обходит при использовании атрибута handleerror, что означает, что он не будет см., что была ошибка, потому что она была обработана уже с помощью атрибута... Что я спрашиваю, есть ли способ заставить ELMAH видеть ошибку и записывать ее, даже если атрибут обрабатывал ее... Я искал вокруг и не делал см. любые способы вызова, чтобы заставить его регистрировать ошибку....

Ответ 1

Вы можете подклассы HandleErrorAttribute и переопределить его OnException (нет необходимости копировать), чтобы он регистрировал исключение с помощью ELMAH и только в том случае, если базовая реализация обрабатывает его. Минимальное количество требуемого кода выглядит следующим образом:

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) 
            return;
        var httpContext = context.HttpContext.ApplicationInstance.Context;
        var signal = ErrorSignal.FromContext(httpContext);
        signal.Raise(context.Exception, httpContext);
    }
}

Сначала запускается базовая реализация, позволяющая отмечать исключение как обрабатываемое. Только тогда это сообщение об ошибке. Вышеприведенный код прост и может вызвать проблемы при использовании в среде, где HttpContext может быть недоступен, например, тестирование. В результате вам понадобится код, который является более защитным (ценой немного длиннее):

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       // if unhandled, will be logged anyhow
            || TryRaiseErrorSignal(context) // prefer signaling, if possible
            || IsFiltered(context))         // filtered?
            return;

        LogException(context);
    }

    private static bool TryRaiseErrorSignal(ExceptionContext context)
    {
        var httpContext = GetHttpContextImpl(context.HttpContext);
        if (httpContext == null)
            return false;
        var signal = ErrorSignal.FromContext(httpContext);
        if (signal == null)
            return false;
        signal.Raise(context.Exception, httpContext);
        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, 
                              GetHttpContextImpl(context.HttpContext));
        return config.Assertion.Test(testContext);
    }

    private static void LogException(ExceptionContext context)
    {
        var httpContext = GetHttpContextImpl(context.HttpContext);
        var error = new Error(context.Exception, httpContext);
        ErrorLog.GetDefault(httpContext).Log(error);
    }

    private static HttpContext GetHttpContextImpl(HttpContextBase context)
    {
        return context.ApplicationInstance.Context;
    }
}

Эта вторая версия попытается сначала использовать сигнализацию об ошибках от ELMAH, которая включает полностью сконфигурированный конвейер, такой как ведение журнала, отправка по почте, фильтрация и вы. В противном случае он пытается проверить, следует ли фильтровать ошибку. Если нет, ошибка просто регистрируется. Эта реализация не обрабатывает почтовые уведомления. Если исключение может быть сигнализировано, тогда будет отправлено письмо, если оно настроено для этого.

Возможно, вам также придется позаботиться о том, что если несколько экземпляров HandleErrorAttribute действуют, то дублирование ведения журнала не происходит, но приведенные выше два примера должны начать работу.

Ответ 2

Извините, но я думаю, что принятый ответ - перебор. Все, что вам нужно сделать, это следующее:

public class ElmahHandledErrorLoggerFilter : IExceptionFilter
{
    public void OnException (ExceptionContext context)
    {
        // Log only handled exceptions, because all other will be caught by ELMAH anyway.
        if (context.ExceptionHandled)
            ErrorSignal.FromCurrentContext().Raise(context.Exception);
    }
}

а затем зарегистрируйте его (порядок важен) в Global.asax.cs:

public static void RegisterGlobalFilters (GlobalFilterCollection filters)
{
    filters.Add(new ElmahHandledErrorLoggerFilter());
    filters.Add(new HandleErrorAttribute());
}

Ответ 3

В NuGet теперь есть пакет ELMAH.MVC, который включает улучшенное решение от Atif, а также контроллер, который обрабатывает интерфейс elmah в маршрутизации MVC (больше не нужно использовать этот axd)
Проблема с этим решением (и со всеми этими здесь) заключается в том, что так или иначе обработчик ошибок elmah фактически обрабатывает ошибку, игнорируя то, что вы можете настроить как тэг customError, или через ErrorHandler или собственный обработчик ошибок < ш > Лучшим решением IMHO является создание фильтра, который будет действовать в конце всех других фильтров и зарегистрировать уже обработанные события. Модуль elmah должен заботиться о регистрации других ошибок, которые не обрабатываются приложением. Это также позволит вам использовать монитор работоспособности и все другие модули, которые могут быть добавлены в asp.net для просмотра событий ошибок.

Я написал это с рефлектором в ErrorHandler внутри elmah.mvc

public class ElmahMVCErrorFilter : IExceptionFilter
{
   private static ErrorFilterConfiguration _config;

   public void OnException(ExceptionContext context)
   {
       if (context.ExceptionHandled) //The unhandled ones will be picked by the elmah module
       {
           var e = context.Exception;
           var context2 = context.HttpContext.ApplicationInstance.Context;
           //TODO: Add additional variables to context.HttpContext.Request.ServerVariables for both handled and unhandled exceptions
           if ((context2 == null) || (!_RaiseErrorSignal(e, context2) && !_IsFiltered(e, context2)))
           {
            _LogException(e, context2);
           }
       }
   }

   private static bool _IsFiltered(System.Exception e, System.Web.HttpContext context)
   {
       if (_config == null)
       {
           _config = (context.GetSection("elmah/errorFilter") as ErrorFilterConfiguration) ?? new ErrorFilterConfiguration();
       }
       var context2 = new ErrorFilterModule.AssertionHelperContext((System.Exception)e, context);
       return _config.Assertion.Test(context2);
   }

   private static void _LogException(System.Exception e, System.Web.HttpContext context)
   {
       ErrorLog.GetDefault((System.Web.HttpContext)context).Log(new Elmah.Error((System.Exception)e, (System.Web.HttpContext)context));
   }


   private static bool _RaiseErrorSignal(System.Exception e, System.Web.HttpContext context)
   {
       var signal = ErrorSignal.FromContext((System.Web.HttpContext)context);
       if (signal == null)
       {
           return false;
       }
       signal.Raise((System.Exception)e, (System.Web.HttpContext)context);
       return true;
   }
}

Теперь в конфигурации фильтра вы хотите сделать что-то вроде этого:

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        //These filters should go at the end of the pipeline, add all error handlers before
        filters.Add(new ElmahMVCErrorFilter());
    }

Обратите внимание, что я оставил комментарий там, чтобы напомнить людям, что если они хотят добавить глобальный фильтр, который будет обрабатывать исключение, он должен перейти к последнему фильтру, иначе вы столкнетесь с тем, что необработанное исключение будет проигнорировано ElmahMVCErrorFilter, потому что он не был обработан, и он должен быть зарегистрирован модулем Elmah, но затем следующий фильтр отмечает исключение, как обрабатывается, и модуль игнорирует его, в результате исключение никогда не превращает его в elmah.

Теперь убедитесь, что настройки для elmah в вашем webconfig выглядят примерно так:

<add key="elmah.mvc.disableHandler" value="false" /> <!-- This handles elmah controller pages, if disabled elmah pages will not work -->
<add key="elmah.mvc.disableHandleErrorFilter" value="true" /> <!-- This uses the default filter for elmah, set to disabled to use our own -->
<add key="elmah.mvc.requiresAuthentication" value="false" /> <!-- Manages authentication for elmah pages -->
<add key="elmah.mvc.allowedRoles" value="*" /> <!-- Manages authentication for elmah pages -->
<add key="elmah.mvc.route" value="errortracking" /> <!-- Base route for elmah pages -->

Важным здесь является "elmah.mvc.disableHandleErrorFilter", если он является ложным, он будет использовать обработчик внутри elmah.mvc, который фактически обработает исключение, используя HandleErrorHandler по умолчанию, который будет игнорировать ваши настройки настраиваемого параметра

Эта настройка позволяет вам устанавливать свои собственные теги ErrorHandler в классах и представлениях, сохраняя при этом все эти ошибки через ElmahMVCErrorFilter, добавляя конфигурацию customError к вашему web.config через модуль elmah, даже записывая собственные обработчики ошибок. Единственное, что вам нужно сделать, это не добавлять фильтры, которые фактически будут обрабатывать ошибку перед фильтром elmah, который мы написали. И я забыл упомянуть: в Эльме нет дубликатов.

Ответ 4

Вы можете взять код выше и сделать еще один шаг, представив пользовательский контроллер factory, который вводит атрибут HandleErrorWithElmah в каждый контроллер.

Для получения дополнительной информации ознакомьтесь с моей серией блога о регистрации в MVC. В первой статье рассматривается создание и запуск Elmah для MVC.

В конце статьи есть ссылка на загружаемый код. Надеюсь, что это поможет.

http://dotnetdarren.wordpress.com/

Ответ 5

Я новичок в ASP.NET MVC. Я столкнулся с той же проблемой, что и в моем Erorr.vbhtml(это работает, если вам нужно только регистрировать ошибку, используя журнал Elmah)

@ModelType System.Web.Mvc.HandleErrorInfo

    @Code
        ViewData("Title") = "Error"
        Dim item As HandleErrorInfo = CType(Model, HandleErrorInfo)
        //To log error with Elmah
        Elmah.ErrorLog.GetDefault(HttpContext.Current).Log(New Elmah.Error(Model.Exception, HttpContext.Current))
    End Code

<h2>
    Sorry, an error occurred while processing your request.<br />

    @item.ActionName<br />
    @item.ControllerName<br />
    @item.Exception.Message
</h2> 

Это просто!

Ответ 6

Полностью альтернативное решение - не использовать MVC HandleErrorAttribute, а вместо этого полагаться на обработку ошибок ASP.Net, с которой Elmah предназначен для работы.

Вам нужно удалить глобальный HandleErrorAttribute по умолчанию из App_Start\FilterConfig (или Global.asax), а затем настроить страницу с ошибкой в ​​вашем Web.config:

<customErrors mode="RemoteOnly" defaultRedirect="~/error/" />

Обратите внимание, что это может быть URL-адрес маршрутизируемого MVC, поэтому приведенное выше будет перенаправлено на действие ErrorController.Index при возникновении ошибки.

Ответ 7

Для меня было очень важно работать с электронной почтой. Через некоторое время я обнаружил, что для этого нужно всего лишь 2 строки кода в примере Atif.

public class HandleErrorWithElmahAttribute : HandleErrorAttribute
{
    static ElmahMVCMailModule error_mail_log = new ElmahMVCMailModule();

    public override void OnException(ExceptionContext context)
    {
        error_mail_log.Init(HttpContext.Current.ApplicationInstance);
        [...]
    }
    [...]
}

Надеюсь, это поможет кому-то:)

Ответ 8

Это именно то, что мне нужно для настройки моего сайта MVC!

Я добавил небольшую модификацию к методу OnException для обработки нескольких экземпляров HandleErrorAttribute, как это было предложено Атифом Азизом:

помните, что вам, возможно, придется позаботиться о том, что если несколько экземпляров HandleErrorAttribute действуют, то дублирование ведения журнала не происходит.

Я просто проверяю context.ExceptionHandled перед вызовом базового класса, просто чтобы узнать, обработал ли кто-то другой исключение перед текущим обработчиком.
Он работает для меня, и я отправляю код на тот случай, если кому-то это понадобится, и спросить, знает ли кто-нибудь, не пропустил ли я что-нибудь.

Надеюсь, что это полезно:

public override void OnException(ExceptionContext context)
{
    bool exceptionHandledByPreviousHandler = context.ExceptionHandled;

    base.OnException(context);

    Exception e = context.Exception;
    if (exceptionHandledByPreviousHandler
        || !context.ExceptionHandled  // if unhandled, will be logged anyhow
        || RaiseErrorSignal(e)        // prefer signaling, if possible
        || IsFiltered(context))       // filtered?
        return;

    LogException(e);
}