Comment faire travailler ELMAH ASP.NET attribut MVC [HandleError]?

j'essaie D'utiliser ELMAH pour enregistrer les erreurs dans mon ASP.NET MVC application, cependant quand j'utilise l'attribut [HandleError] sur mes controllers ELMAH n'enregistre pas les erreurs quand elles se produisent.

comme je suppose que c'est parce que ELMAH ne enregistre que les erreurs non corrigées et que l'attribut [HandleError] gère l'erreur donc pas besoin de le faire.

comment modifier ou comment modifier l'attribut pour QU'ELMAH sache qu'il y a une erreur et le journal..

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

557
demandé sur dswatik 2009-04-20 06:09:11

8 réponses

vous pouvez sous-classe HandleErrorAttribute et remplacer son OnException membre (pas besoin de copier) de sorte 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 invoquée en premier, lui donnant une chance de marquer l'exception comme étant traitée. Ce n'est qu'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, tels que les essais. En conséquence, vous voudrez le code qui est qui est plus défensif (au coût d'être un peu 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 seconde version va essayer d'utiliser message d'erreur D'ELMAH d'abord, ce qui implique le pipeline entièrement configuré comme la journalisation, Le mailing, Le filtrage et ce que vous avez. A défaut, elle tente de voir si la erreur doit être filtré. Si non, l'erreur est simplement connecté. Cette implémentation ne traite pas les notifications par courrier. Si l'exception peut être signalée alors un mail sera envoyé si configuré pour le faire.

vous pouvez également prendre soin que si plusieurs HandleErrorAttribute instances sont en vigueur, alors la journalisation en double ne se produit pas, mais les deux exemples ci-dessus devraient vous permettre de commencer.

496
répondu Atif Aziz 2012-06-06 12:03:19

désolé, mais je pense que la réponse acceptée est exagérée. 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);
    }
}

et ensuite l'enregistrer (l'ordre est important) dans Global.asax.cs:

public static void RegisterGlobalFilters (GlobalFilterCollection filters)
{
    filters.Add(new ElmahHandledErrorLoggerFilter());
    filters.Add(new HandleErrorAttribute());
}
297
répondu Ivan Zlatev 2011-05-09 12:27:32

il y a maintenant un ELMAH.Paquet MVC dans NuGet qui comprend 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 cet axd)

Le problème avec cette solution (et avec toutes les autres ici) est que d'une manière ou d'une autre le gestionnaire d'erreurs elmah est en train de gérer l'erreur, ignorant ce que vous pourriez vouloir configurer comme une étiquette customError ou via ErrorHandler ou votre propre gestionnaire d'erreurs

La meilleure solution IMHO est de créer un filtre qui agira à la fin de tous les autres filtres et enregistrer les événements qui ont déjà été traités. Le module elmah doit prendre soin d'enregistrer les autres erreurs qui ne sont pas gênées par l'application. Cela vous permettra également d'utiliser le moniteur de la santé et tous les autres modules qui peuvent être ajoutés à asp.net à regarder les événements d'erreur

j'ai écrit ceci en regardant avec réflecteur L'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 pour rappeler aux gens que s'ils veulent ajouter un filtre global qui gérera effectivement l'exception, il devrait passer avant ce dernier filtre, sinon vous courrez dans le cas où L'exception non gérée sera ignorée par le filtre ElmahMVCErrorFilter parce qu'elle n'a pas été traitée et qu'elle devrait être logée par le module Elmah mais le filtre suivant marque l'exception telle qu'elle est manipulée et le module l'ignore, ce qui fait que l'exception ne se transforme jamais en elmah.

maintenant, assurez-vous que les applications pour elmah dans votre webconfig ressemblent à quelque chose comme 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 ceci est faux, il utilisera le handler à l'intérieur d'elmah.mvc qui va réellement gérer 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 par le biais de L'ElmahMVCErrorFilter, en ajoutant une configuration customError à votre web.config via le module elmah, même en écrivant vos propres gestionnaires d'erreurs. La seule chose que vous devez faire est de ne pas ajouter de filtres qui seront réellement gérer l'erreur avant le filtre elmah que nous avons écrit. Et j'ai oublié de mentionner: aucun duplicata à elmah.

14
répondu Raul Vejar 2013-01-24 18:10:41

vous pouvez prendre le code ci-dessus et aller un peu plus loin en introduisant une usine de contrôleur personnalisé qui injecte l'attribut HandleErrorWithElmah dans chaque contrôleur.

pour plus d'informations, consultez ma série de blogs sur l'ouverture de session dans MVC. Le premier article traite de l'installation et de la mise en service d'Elmah pour MVC.

il y a un lien vers le code téléchargeable à la fin de l'article. Espérons que cela aide.

http://dotnetdarren.wordpress.com /

7
répondu Darren 2010-07-28 00:19:15

je suis nouveau dans ASP.NET MVC. J'ai connu le même problème, voici mon réalisable dans mon Erorr.vbhtml (ça marche si vous devez vous connecter à l'erreur à l'aide de Elmah journal)

@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 tout simplement!

6
répondu user716264 2011-04-20 01:40:02

une solution complètement alternative est de ne pas utiliser le MVC HandleErrorAttribute , et à la place de compter sur ASP.Net la gestion des erreurs, avec laquelle Elmah est conçu pour travailler.

vous devez supprimer le global par défaut HandleErrorAttribute de App_Start\FilterConfig (ou Global.asax), et ensuite configurer une page d'erreur dans votre Web.config:

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

Note, Ceci peut être une URL MVC routée, donc ce qui précède redirigerait vers l'action ErrorController.Index quand une erreur se produit.

6
répondu Ross McNab 2013-08-01 07:32:20

pour moi, il était très important de faire fonctionner la journalisation par e-mail. Après un certain temps, je découvre que cela n'a besoin que de 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 va aider quelqu'un :)

5
répondu Komio 2011-01-26 09:11:26

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

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

gardez à l'esprit que vous pouvez prendre soin que si plusieurs HandleErrorAttribute instances sont en vigueur, alors la journalisation en double ne se produit pas.

je coche simplement context.ExceptionHandled avant invoquer la classe de base, juste pour savoir si quelqu'un d'autre a manipulé l'exception avant le gestionnaire actuel.

Cela fonctionne pour moi et je poste le code au cas où quelqu'un d'autre en a besoin 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
répondu ilmatte 2010-11-06 22:35:52