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

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

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

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

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

20.04.2009 02:09:11
Надеюсь, Джефф или Джаред ответят на этот вопрос. Они используют ELMAH для Stackoverflow;)
Jon Limjap 20.04.2009 05:44:44
Хм, странно - мы не используем HandleErrorAttribute - Elmah настроен в разделе <modules> нашего web.config. Есть ли преимущества использования HandleErrorAttribute?
Jarrod Dixon♦ 22.04.2009 03:39:08
@Jarrod - было бы приятно узнать, что "обычного" в вашей вилке ELMAH.
Scott Hanselman 24.04.2009 00:07:57
@dswatik Вы также можете предотвратить перенаправления, установив для redirectMode значение ResponseRewrite в web.config. Смотрите blog.turlov.com/2009/01/…
Pavel Chuchuva 26.07.2010 06:49:55
Я продолжал работать с веб-документацией и сообщениями, рассказывающими об атрибуте [HandleError] и Elmah, но я не видел поведения, которое это решает (например, Elmah не регистрирует «обработанную» ошибку) при настройке фиктивного случая. Это связано с тем, что в Elmah.MVC 2.0.x этот пользовательский атрибут HandleErrorAttribute больше не требуется; это включено в пакет nuget.
plyawn 13.10.2012 21:14:59
8 ОТВЕТОВ
РЕШЕНИЕ

Вы можете создать подкласс 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действуют несколько экземпляров, дублирование регистрации не происходило, но приведенные выше два примера должны помочь вам начать работу.

503
6.06.2012 12:03:19
Отлично. Я вообще не пытался реализовать Эльму. Я просто пытался подключить свои собственные отчеты об ошибках, которые я использовал годами, таким образом, который хорошо работает с MVC. Ваш код дал мне отправную точку. +1
Steve Wortham 18.11.2009 03:29:50
Вам не нужно создавать подкласс HandleErrorAttribute. Вы можете просто иметь реализацию IExceptionFilter и зарегистрировать ее вместе с HandleErrorAttribute. Также я не понимаю, зачем вам нужен запасной вариант в случае сбоя ErrorSignal.Raise (..). Если конвейер плохо настроен, его следует исправить. Для 5-го лайнера IExceptionFilter проверьте пункт 4. здесь - ivanz.com/2011/05/08/…
Ivan Zlatev 9.05.2011 12:22:55
Пожалуйста, вы можете прокомментировать ответ ниже @IvanZlatev относительно применимости, недостатков и т. Д. Люди комментируют, что это легче / короче / проще и достигает того же, что и ваш ответ, и поэтому должно быть помечено как правильный ответ. Было бы хорошо узнать ваше мнение об этом и добиться некоторой ясности с этими ответами.
Andrew 22.06.2012 16:39:12
Это все еще актуально или ELMAH.MVC справляется с этим?
Romias 3.04.2014 13:45:09
Даже я хотел бы знать, актуально ли это в сегодняшней версии
refactor 15.06.2015 10:16:12

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

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

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

http://dotnetdarren.wordpress.com/

7
28.07.2010 00:19:15
Мне кажется, что было бы намного проще просто прикрепить его к базовому классу контроллеров!
Nathan Taylor 28.07.2010 06:30:00
Вышеприведенная серия статей Даррена о ведении журнала и обработке исключений стоит того, чтобы ее прочитать !!! Очень тщательно!
Ryan Anderson 19.08.2010 05:58:37

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

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

имейте в виду, что вам, возможно, придется позаботиться о том, чтобы, если 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);
}
2
6.11.2010 22:35:52
Похоже, у вас нет оператора «if» вокруг вызова base.OnException () .... И (exceptionHandledByPreviousHandler ||! Context.ExceptionHandled || ...) отменяют друг друга и всегда будут истинными. Я что-то пропустил?
joelvh 18.01.2011 18:17:25
Сначала я проверяю, управлял ли исключение какой-либо другой обработчик, вызванный до текущего, и сохраняю результат в переменной: exceptionHandlerdByPreviousHandler. Затем я даю возможность текущему обработчику управлять самим исключением: base.OnException (context).
ilmatte 13.06.2011 08:31:47
Сначала я проверяю, управлял ли исключение какой-либо другой обработчик, вызванный до текущего, и сохраняю результат в переменной: exceptionHandlerdByPreviousHandler. Затем я даю возможность текущему обработчику управлять самим исключением: base.OnException (context). Если исключение ранее не управлялось, оно может быть: 1 - оно управляется текущим обработчиком, затем: exceptionHandledByPreviousHandler = false и! Context.ExceptionHandled = false 2 - оно не управляется текущим обработчиком и: exceptionHandledByPreviousHandler = false и! Context. Исключение Только случай 1 будет регистрироваться.
ilmatte 13.06.2011 08:39:07

Для меня было очень важно, чтобы регистрация электронной почты работала. Через некоторое время я обнаружил, что для этого нужно всего 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);
        [...]
    }
    [...]
}

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

5
26.01.2011 09:11:26

Я новичок в 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
20.04.2011 01:40:02
Это, безусловно, самое простое решение. Не нужно писать или регистрировать пользовательские обработчики и прочее. У меня отлично работает
ThiagoAlves 27.08.2011 15:38:22
Будет игнорироваться для любых ответов JSON / не HTML.
Craig Stuntz 13.01.2012 22:33:47
также это делает функциональность уровня обслуживания в представлении. Здесь не место
Trevor de Koekkoek 30.12.2012 02:31:36

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

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());
}
298
9.05.2011 12:27:32
+1 Очень приятно, не нужно расширять HandleErrorAttribute, не нужно переопределять OnExceptionдальше BaseController. Это предполагает принятый ответ.
CallMeLaNN 10.06.2011 07:35:54
@bigb Я думаю, вам нужно будет обернуть исключение в свой собственный тип исключения, чтобы добавить что-либо к сообщению об исключении и т. д. (например, new UnhandledLoggedException(Exception thrown)который добавляет что-то к Messageпредыдущему.
Ivan Zlatev 27.07.2011 22:24:14
Атиф Азиз создал ELMAH, я бы пошел с его ответом
jamiebarrow 5.08.2011 09:01:04
@jamiebarrow Я не осознавал этого, но его ответу ~ 2 года, и, вероятно, API был упрощен, чтобы поддержать варианты использования вопроса более коротким и автономным способом.
Ivan Zlatev 5.08.2011 20:38:24
@ Иван Златев действительно не может добраться до работы, ElmahHandledErrorLoggerFilter()просто записывая необработанные ошибки, но не обработанные. Я зарегистрировал фильтры в правильном порядке, как вы упомянули, какие-нибудь мысли?
kuncevic.dev 14.09.2011 06:21:54

Теперь в 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.

Теперь убедитесь, что настройки app для 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», если это false, он будет использовать обработчик внутри elmah.mvc, который фактически обработает исключение с помощью HandleErrorHandler по умолчанию, который будет игнорировать ваши настройки customError

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

14
24.01.2013 18:10:41

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

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

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

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

6
1.08.2013 07:32:20
На сегодняшний день это самое простое решение, и перенаправление по умолчанию может быть действием MVC :)
Jeremy Cook 29.08.2013 21:44:53
Это будет перенаправлять для других типов запросов, таких как JSON и т. Д. - не очень хорошо.
zvolkov 14.02.2014 21:42:11