Cómo hacer que ELMAH trabaje con ASP.NET MVC [HandleError] atributo?


Estoy tratando de usar ELMAH para registrar errores en mi ASP.NET La aplicación MVC, sin embargo, cuando uso el atributo [HandleError] en mis controladores, ELMAH no registra ningún error cuando se producen.

Como supongo, es porque ELMAH solo registra los errores no manejados y el atributo [HandleError] está manejando el error, por lo que no es necesario registrarlo.

¿ Cómo modifico o cómo modificaría el atributo para que ELMAH pueda saber que hubo un error y un registro se..

Edit: Déjame asegurarme de que todo el mundo entiende, sé que puedo modificar el atributo que no es la pregunta que estoy haciendo... ELMAH se omite cuando se usa el atributo handleerror, lo que significa que no verá que hubo un error porque ya fue manejado por el atributo... Lo que estoy preguntando es que hay una manera de hacer que ELMAH vea el error y lo registre a pesar de que el atributo lo manejó...Busqué alrededor y no veo ningún método para llamar para forzarlo a registrar el error....

Author: dswatik, 2009-04-20

8 answers

Puedes subclase HandleErrorAttribute y anular su OnException miembro (no es necesario copiar) para que registre la excepción con ELMAH y solo si la implementación base la maneja. La cantidad mínima de código que necesita es la siguiente:

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

La implementación base se invoca primero, dándole la oportunidad de marcar la excepción como manejada. Solo entonces se señala la excepción. El código anterior es simple y puede causar problemas si se usa en un entorno donde HttpContext puede no ser disponible, como pruebas. Como resultado, querrá un código que sea más defensivo (a costa de ser un poco más largo):

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

Esta segunda versión intentará usar señalización de error desde ELMAH primero, lo que implica la canalización completamente configurada como registro, correo, filtrado y lo que tenga. En su defecto, intenta ver si el error debe filtrarse. Si no, el error simplemente se registra. Esta implementación no maneja notificaciones por correo. Si el la excepción puede ser señalada y luego se enviará un correo si está configurado para hacerlo.

Es posible que también tenga cuidado de que si hay varias instancias HandleErrorAttribute en efecto, no se produzca un registro duplicado, pero los dos ejemplos anteriores deberían comenzar.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-06 12:03:19

Lo siento, pero creo que la respuesta aceptada es un exceso. Todo lo que necesitas hacer es esto:

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

Y luego registrarlo (el orden es importante) en 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-09 12:27:32

Ahora hay un ELMA.Paquete MVC en NuGet que incluye una solución mejorada por Atif y también un controlador que maneja la interfaz elmah dentro del enrutamiento MVC (ya no es necesario usar ese axd)
El problema con esa solución (y con todas las que están aquí) es que de una forma u otra el controlador de errores elmah está manejando el error, ignorando lo que podría querer configurar como una etiqueta CustomError o a través de ErrorHandler o su propio controlador de errores
La mejor solución en mi humilde opinión es cree un filtro que actuará al final de todos los otros filtros y registre los eventos que ya se han manejado. El módulo elmah debe encargarse de registrar los otros errores que no son manejados por la aplicación. Esto también le permitirá utilizar el monitor de salud y todos los otros módulos que se pueden agregar a asp.net para ver los eventos de error

Escribí esto mirando con reflector al ErrorHandler dentro de 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;
   }
}

Ahora, en su configuración de filtro que desea hacer algo como esto:

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

Observe que dejé un comentario allí para recordar a la gente que si quieren agregar un filtro global que realmente maneje la excepción, debería ir ANTES de este último filtro, de lo contrario se encontrará con el caso en el que la excepción no manejada será ignorada por el filtro Elmahmvcerror porque no ha sido manejada y debe ser registrada por el módulo Elmah, pero luego el siguiente filtro marca la excepción como manejada y el módulo la ignora, lo que resulta en la excepción nunca lo convertiré en elmah.

Ahora, asegúrese de que los ajustes de aplicaciones para elmah en su webconfig se vean algo como esto:

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

El importante aquí es "elmah.mvc.disableHandleErrorFilter", si esto es false utilizará el controlador dentro de elmah.mvc que realmente manejará la excepción mediante el HandleErrorHandler predeterminado que ignorará la configuración de CustomError

Esta configuración le permite establecer sus propias etiquetas ErrorHandler en clases y vistas, mientras sigue registrando esos errores a través del filtro Elmahmvcerror, añadiendo una configuración CustomError a tu web.config a través del módulo elmah, incluso escribiendo sus propios Controladores de errores. Lo único que necesita hacer es recordar no agregar ningún filtro que realmente maneje el error antes del filtro elmah que hemos escrito. Y olvidé mencionar: no hay duplicados en 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-24 18:10:41

Puede tomar el código anterior e ir un paso más allá introduciendo una fábrica de controladores personalizados que inyecta el atributo HandleErrorWithElmah en cada controlador.

Para obtener más información, echa un vistazo a mi serie de blogs sobre el inicio de sesión en MVC. El primer artículo cubre cómo configurar Elmah y ejecutarlo para MVC.

Hay un enlace al código descargable al final del artículo. Espero que eso ayude.

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-28 00:19:15

Soy nuevo en ASP.NET MVC. Me enfrenté al mismo problema, el siguiente es mi realizable en mi Erorr.vbhtml (funciona si solo necesita registrar el error usando 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> 

Es simplemente!

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-20 01:40:02

Una solución completamente alternativa es no usar el MVC HandleErrorAttribute, y en su lugar confiar en ASP.Net manejo de errores, con el que Elmah está diseñado para funcionar.

Debe eliminar el valor predeterminado global HandleErrorAttribute de App_Start\FilterConfig (o Global.asax), y luego configurar una página de error en su Web.config:

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

Tenga en cuenta que esto puede ser una URL enrutada MVC, por lo que lo anterior redirigiría a la acción ErrorController.Index cuando se produce un error.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-01 07:32:20

Para mí era muy importante que el registro de correo electrónico funcionara. Después de algún tiempo descubro que esta necesidad solo 2 líneas de código más en Atif ejemplo.

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

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

Espero que esto ayude a alguien :)

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-26 09:11:26

¡Esto es exactamente lo que necesitaba para la configuración de mi sitio MVC!

Agregué una pequeña modificación al método OnException para manejar múltiples instancias HandleErrorAttribute, como sugirió Atif Aziz:

Tenga en cuenta que puede tener que tener cuidado de que si hay varias instancias HandleErrorAttribute en efecto, no se produzca un registro duplicado.

Simplemente compruebo context.ExceptionHandled antes de invocar la clase base, solo para saber si alguien más manejó la excepción antes del manejador actual.
Se funciona para mí y publico el código en caso de que alguien más lo necesite y para preguntar si alguien sabe si pasé por alto algo.

Espero que sea útil:

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-06 22:35:52