Jak zmusić ELMAH do pracy z ASP.NET atrybut MVC [HandleError]?

Próbuję użyć ELMAH do logowania błędów w moim ASP.NET aplikacja MVC, jednak gdy używam atrybutu [HandleError] na moich kontrolerach ELMAH nie rejestruje żadnych błędów, gdy występują.

Jak zgaduję, ponieważ ELMAH rejestruje tylko nieobsługiwane błędy, a atrybut [HandleError] obsługuje błąd, więc nie ma potrzeby go logować.

Jak zmodyfikować lub jak zmienić atrybut, aby ELMAH wiedział, że wystąpił błąd i log to..

Edit: upewnię się, że wszyscy rozumieją, wiem, że mogę zmodyfikować atrybut, który nie jest pytaniem, które zadaję... ELMAH zostaje pominięta podczas używania atrybutu handleerror, co oznacza, że nie zobaczy, że wystąpił błąd, ponieważ był już obsługiwany przez atrybut... Pytam o to, czy istnieje sposób, aby elmah zobaczył błąd i zalogował go, mimo że atrybut go obsługiwał...Szukałem i nie widzę żadnych metod, aby wywołać, aby zmusić go do zalogowania błędu....

Author: dswatik, 2009-04-20

8 answers

Możesz podklasować HandleErrorAttribute i nadpisać jego OnException member (nie ma potrzeby kopiowania) tak, aby zapisywał wyjątek z ELMAH i tylko wtedy, gdy implementacja bazowa go obsługuje. Minimalna ilość potrzebnego kodu jest następująca:

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

Implementacja bazowa jest wywoływana jako pierwsza, co daje jej szansę na zaznaczenie wyjątku jako obsługiwanego. Dopiero wtedy sygnalizowany jest wyjątek. Powyższy kod jest prosty i może powodować problemy, jeśli jest używany w środowisku, w którym HttpContext może nie być dostępne, takie jak testowanie. W rezultacie będziesz chciał kodu, który będzie bardziej Defensywny (kosztem bycia nieco dłuższym): {]}

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

Ta druga wersja spróbuje użyć sygnalizacji błędów z elmah first, która obejmuje w pełni skonfigurowany rurociąg, taki jak logowanie, mailing, filtrowanie i co masz. W przeciwnym razie próbuje sprawdzić, czy błąd powinien być filtrowany. Jeśli nie, błąd jest po prostu rejestrowany. Ta implementacja nie obsługuje powiadomień mailowych. Jeśli wyjątek może być sygnalizowany, a następnie wiadomość zostanie wysłana, jeśli jest tak skonfigurowana.

Być może będziesz musiał zadbać o to, aby w przypadku wystąpienia wielu instancji HandleErrorAttribute nie doszło do zduplikowanego logowania, ale powyższe dwa przykłady powinny zacząć działać.

 496
Author: Atif Aziz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-06 12:03:19

Przepraszam, ale myślę, że przyjęta odpowiedź to przesada. Wszystko co musisz zrobić to:

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

A następnie zarejestrować go (kolejność jest ważna) w Global.asax.cs:

public static void RegisterGlobalFilters (GlobalFilterCollection filters)
{
    filters.Add(new ElmahHandledErrorLoggerFilter());
    filters.Add(new HandleErrorAttribute());
}
 297
Author: Ivan Zlatev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-09 12:27:32

Jest teraz ELMAH.Pakiet MVC w NuGet, który zawiera ulepszone rozwiązanie Atif, a także kontroler obsługujący interfejs elmah w ramach routingu MVC (nie trzeba już używać axd)
Problem z tym rozwiązaniem (i ze wszystkimi tutaj) polega na tym, że w ten czy inny sposób obsługa błędu elmah faktycznie obsługuje błąd, ignorując to, co możesz chcieć skonfigurować jako tag customError lub poprzez ErrorHandler lub własny handler błędów
Najlepszym rozwiązaniem IMHO jest Utwórz filtr, który będzie działał na końcu wszystkich innych filtrów i rejestruje zdarzenia, które zostały już obsługiwane. Moduł elmah powinien zająć się logowaniem innych błędów, które nie są obsługiwane przez aplikację. Pozwoli to również na korzystanie z monitora zdrowia i wszystkich innych modułów, które można dodać do asp.net aby spojrzeć na zdarzenia błędów

Napisałem to patrząc z reflektorem na ErrorHandler wewnątrz 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;
   }
}

Teraz, w konfiguracji filtra chcesz zrobić coś takiego:

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

Zauważ, że zostawiłem tam komentarz, aby przypomnieć ludziom, że jeśli chcą dodać globalny filtr, który faktycznie będzie obsługiwał wyjątek, powinien przejść przed tym ostatnim filtrem, w przeciwnym razie napotkasz przypadek, w którym nieobsługiwany wyjątek będzie ignorowany przez ElmahMVCErrorFilter, ponieważ nie został obsługiwany i powinien być zalogowany przez moduł Elmah, ale następnie następny filtr zaznacza wyjątek jako obsługiwany i moduł go ignoruje, co spowoduje, że wyjątek zostanie zignorowany przez elmahmvcerrorfilter. nigdy nie dostał się do elmah.

Teraz upewnij się, że appsettings dla elmah w webconfig wyglądają mniej więcej tak:

<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 -->
Ważne jest tutaj "elmah.mvc.disableHandleErrorFilter", jeśli jest to false, użyje obsługi wewnątrz elmah.mvc, który faktycznie obsłuży wyjątek, używając domyślnego HandleErrorHandler, który zignoruje ustawienia customError

Ta konfiguracja pozwala ustawić własne znaczniki ErrorHandler w klasach i widokach, podczas logowania te błędy przez ElmahMVCErrorFilter, dodając konfigurację customError do twojej sieci.config poprzez moduł elmah, nawet pisząc własne narzędzia do obsługi błędów. Jedyne co musisz zrobić, to pamiętać, aby nie dodawać żadnych filtrów, które faktycznie obsłuży błąd przed filtrem elmah, który napisaliśmy. I zapomniałem wspomnieć: żadnych duplikatów w elmah.

 14
Author: Raul Vejar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-24 18:10:41

Możesz wziąć powyższy kod i pójść o krok dalej, wprowadzając niestandardową fabrykę kontrolerów, która wstrzykuje atrybut HandleErrorWithElmah do każdego kontrolera.

Aby uzyskać więcej informacji, zajrzyj na mój blog o logowaniu do MVC. Pierwszy artykuł dotyczy konfiguracji Elmah i uruchomienia MVC.

Na końcu artykułu znajduje się link do kodu do pobrania. Mam nadzieję, że to pomoże.

Http://dotnetdarren.wordpress.com/

 7
Author: Darren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-28 00:19:15

Jestem nowy w ASP.NET MVC. Stanąłem w obliczu tego samego problemu, poniżej znajduje się mój wykonalny w moim eror.vbhtml (działa, jeśli musisz tylko zalogować błąd używając elmah log)

@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> 
To po prostu!
 6
Author: user716264,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-20 01:40:02

Całkowicie alternatywnym rozwiązaniem jest nie używać MVC HandleErrorAttribute, a zamiast tego polegać na ASP.Net obsługa błędów, z którymi elmah jest przeznaczony do pracy.

Musisz usunąć domyślną globalną HandleErrorAttribute z App_Start\FilterConfig (lub Global.asax), a następnie skonfiguruj stronę błędu w swojej sieci.config:

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

Uwaga, Może to być adres URL przekierowywany przez MVC, więc powyższy adres przekierowywałby do akcji ErrorController.Index, gdy wystąpi błąd.

 6
Author: Ross McNab,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-01 07:32:20

Dla mnie bardzo ważne było, aby logowanie e-mail działało. Po jakimś czasie odkrywam, że to wymaga tylko 2 linijek kodu więcej w przykładzie 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);
        [...]
    }
    [...]
}

Mam nadzieję, że to komuś pomoże:)

 5
Author: Komio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-26 09:11:26

To jest dokładnie to, czego potrzebowałem do konfiguracji mojej strony MVC!

Dodałem małą modyfikację metody OnException do obsługi wielu instancji HandleErrorAttribute, zgodnie z sugestią Atif Aziz:

Pamiętaj, że być może będziesz musiał zadbać o to, aby w przypadku wystąpienia wielu instancji HandleErrorAttribute nie wystąpi duplikat logowania.

Po prostu sprawdzam context.ExceptionHandled przed wywołaniem klasy bazowej, aby wiedzieć, czy ktoś inny obsługiwał wyjątek przed bieżącym handlerem.
Informatyka działa dla mnie i zamieszczam kod na wypadek, gdyby ktoś inny go potrzebował i pytam, czy ktoś wie, czy coś przeoczyłem.

Mam nadzieję, że się przyda:

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
Author: ilmatte,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-06 22:35:52