Как добраться ELMAH для работы с ASP.NET в MVC метод [handleerror] атрибут?


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

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

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

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

8 557

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

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

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);
    }
}

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

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

теперь есть Эльма.Пакет MVC в NuGet, который включает улучшенное решение Atif, а также контроллер, который обрабатывает интерфейс elmah в маршрутизации MVC (больше не нужно использовать этот axd)
Проблема с этим решением (и со всеми здесь) заключается в том, что так или иначе обработчик ошибок elmah фактически обрабатывает ошибку, игнорируя то, что вы можете настроить в качестве тега customError или через ErrorHandler или свой собственный обработчик ошибок
Лучшим решением имхо является чтобы создать фильтр, который будет действовать в конце всех других фильтров и регистрировать события, которые уже были обработаны. Модуль 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", если это false, он будет использовать обработчик внутри elmah.mvc, который будет фактически обрабатывать исключение с помощью HandleErrorHandler по умолчанию, который будет игнорировать ваши настройки customError

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

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

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

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

http://dotnetdarren.wordpress.com/

Я новичок 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> 

Это просто!

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

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

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

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

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

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

это именно то, что мне нужно для моей конфигурации сайта 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);
}