Question Comment faire fonctionner ELMAH avec l'attribut ASP.NET MVC [HandleError]?


J'essaie d'utiliser ELMAH pour enregistrer les erreurs dans mon application ASP.NET MVC, cependant lorsque j'utilise l'attribut [HandleError] sur mes contrôleurs, ELMAH n'enregistre aucune erreur lorsqu'elles se produisent.

Comme je le devine car ELMAH enregistre uniquement les erreurs non gérées et l'attribut [HandleError] gère l'erreur, il n'est donc pas nécessaire de le consigner.

Comment puis-je modifier ou comment procéder pour modifier l'attribut afin qu'ELMAH puisse savoir qu'il y a eu une erreur et l'enregistrer?

Modifier: Laissez-moi m'assurer que tout le monde comprend, je sais que je peux modifier l'attribut ce n'est pas la question que je pose ... ELMAH est contourné en utilisant l'attribut handleerror ce qui signifie qu'il ne verra pas qu'il y avait une erreur car il a déjà été manipulé l'attribut ... Ce que je demande est là un moyen de faire voir ELMAH l'erreur et de l'enregistrer même si l'attribut l'a manipulé ... J'ai cherché autour et je ne vois aucune méthode à appeler pour le forcer à enregistrer l'erreur ....


556
2018-04-20 02:09


origine


Réponses:


Vous pouvez sous-classer HandleErrorAttribute et remplacer son OnException membre (pas besoin de copier) pour qu'il enregistre l'exception avec ELMAH et seulement si l'implémentation de base le gère. La quantité minimale de code dont vous avez besoin est la suivante:

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

L'implémentation de base est appelée en premier, ce qui lui permet de marquer l'exception comme étant gérée. C'est seulement alors que l'exception est signalée. Le code ci-dessus est simple et peut causer des problèmes si utilisé dans un environnement où le HttpContext peut ne pas être disponible, comme les tests. En conséquence, vous voudrez du code qui soit plus défensif (au prix d'être légèrement plus long):

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

Cette deuxième version va essayer d'utiliser signalisation d'erreur d'abord ELMAH, qui implique le pipeline entièrement configuré comme la journalisation, l'envoi, le filtrage et ce que vous avez. A défaut, il tente de voir si l'erreur doit être filtrée. Sinon, l'erreur est simplement enregistrée. Cette implémentation ne gère pas les notifications par courrier. Si l'exception peut être signalée, un mail sera envoyé s'il est configuré pour le faire.

Vous devrez peut-être également prendre soin que si plusieurs HandleErrorAttribute Les instances sont en vigueur, puis la journalisation en double ne se produit pas, mais les deux exemples ci-dessus devraient vous aider à démarrer.


496
2018-04-23 01:28



Désolé, mais je pense que la réponse acceptée est une overkill. Tout ce que vous devez faire est ceci:

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

puis enregistrez-le (l'ordre est important) dans Global.asax.cs:

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

296
2018-05-09 12:27



Il y a maintenant un paquet ELMAH.MVC dans NuGet qui inclut une solution améliorée par Atif et aussi un contrôleur qui gère l'interface elmah dans le routage MVC (plus besoin d'utiliser cette axd)
Le problème avec cette solution (et avec tous ceux ici) est que d'une manière ou d'une autre le gestionnaire d'erreur d'elmah manipule réellement l'erreur, ignorant ce que vous pourriez vouloir configurer en tant que balise customError ou par ErrorHandler ou votre propre gestionnaire d'erreur
La meilleure solution à mon humble avis consiste à créer un filtre qui agira à la fin de tous les autres filtres et consigner les événements déjà traités. Le module elmah devrait prendre soin de consigner les autres erreurs qui ne sont pas traitées par l'application. Cela vous permettra également d'utiliser le moniteur de santé et tous les autres modules qui peuvent être ajoutés à asp.net pour regarder les événements d'erreur

J'ai écrit ceci en regardant avec le réflecteur au ErrorHandler à l'intérieur d'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;
   }
}

Maintenant, dans votre configuration de filtre, vous voulez faire quelque chose comme ceci:

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

Notez que j'ai laissé un commentaire là pour rappeler aux gens que s'ils veulent ajouter un filtre global qui gérera l'exception, il devrait aller AVANT ce dernier filtre, sinon vous courrez dans le cas où l'exception non gérée sera ignorée par ElmahMVCErrorFilter parce que il n'a pas été manipulé et il doit être enregistré par le module Elmah, mais le filtre suivant marque l'exception comme manipulée et le module l'ignore, ce qui fait que l'exception ne la transforme jamais en elmah.

Maintenant, assurez-vous que les appsettings pour elmah dans votre webconfig ressemblent à ceci:

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

L'important ici est "elmah.mvc.disableHandleErrorFilter", si cela est faux, il utilisera le gestionnaire dans elmah.mvc qui gérera l'exception en utilisant le HandleErrorHandler par défaut qui ignorera vos paramètres customError

Cette configuration vous permet de définir vos propres balises ErrorHandler dans les classes et les vues, tout en enregistrant ces erreurs via ElmahMVCErrorFilter, en ajoutant une configuration customError à votre web.config via le module elmah, en écrivant vos propres gestionnaires d'erreurs. La seule chose que vous devez faire est de ne pas ajouter de filtres qui traiteront l'erreur avant le filtre d'Elmah que nous avons écrit. Et j'ai oublié de mentionner: pas de doublons dans elmah.


14
2018-01-24 18:10



Vous pouvez prendre le code ci-dessus et aller plus loin en introduisant une fabrique de contrôleurs personnalisés qui injecte l'attribut HandleErrorWithElmah dans chaque contrôleur.

Pour plus d'informations, consultez ma série de blogs sur la connexion à MVC. Le premier article couvre la mise en place d'Elmah pour MVC.

Il y a un lien vers le code téléchargeable à la fin de l'article. J'espère que cela pourra aider.

http://dotnetdarren.wordpress.com/


7
2017-07-28 00:19



Je suis nouveau dans ASP.NET MVC. J'ai fait face au même problème, ce qui suit est mon réalisable dans mon Erorr.vbhtml (cela fonctionne si vous avez seulement besoin de vous connecter l'erreur en utilisant le journal d'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> 

C'est simplement!


6
2018-04-20 01:40



Une solution complètement alternative est de ne pas utiliser le MVC HandleErrorAttribute, et s'appuient plutôt sur la gestion des erreurs ASP.Net, avec laquelle Elmah est conçu pour fonctionner.

Vous devez supprimer le paramètre global par défaut HandleErrorAttribute à partir de App_Start \ FilterConfig (ou Global.asax), puis définissez une page d'erreur dans votre Web.config:

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

Notez qu'il peut s'agir d'une URL acheminée par MVC, de sorte que ce qui précède redirige vers ErrorController.Index action quand une erreur se produit.


6
2017-08-01 07:32



Pour moi, il était très important de faire fonctionner la journalisation des emails. Après un certain temps je découvre que cela nécessite seulement 2 lignes de code de plus dans l'exemple 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);
        [...]
    }
    [...]
}

J'espère que cela aidera quelqu'un :)


5
2018-01-26 09:11



C'est exactement ce dont j'avais besoin pour ma configuration de site MVC!

J'ai ajouté une petite modification à la OnException méthode pour gérer plusieurs HandleErrorAttribute instances, comme suggéré par Atif Aziz:

Gardez à l'esprit que vous devrez peut-être veiller à ce que si plusieurs HandleErrorAttribute les instances sont en vigueur, puis la journalisation en double ne se produit pas.

Je vérifie simplement context.ExceptionHandled avant d'appeler la classe de base, juste pour savoir si quelqu'un d'autre a géré l'exception avant le gestionnaire en cours.
Cela fonctionne pour moi et je poste le code au cas où quelqu'un d'autre aurait besoin de lui et de demander si quelqu'un sait si j'ai oublié quelque chose.

J'espère que c'est utile:

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
2017-11-06 22:35