Journalisation des erreurs dans ASP.NET MVC

109

J'utilise actuellement log4net dans mon application ASP.NET MVC pour enregistrer les exceptions. La façon dont je fais cela est de faire hériter tous mes contrôleurs d'une classe BaseController. Dans l'événement OnActionExecuting de BaseController, je consigne toutes les exceptions qui ont pu se produire:

protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
    // Log any exceptions
    ILog log = LogManager.GetLogger(filterContext.Controller.GetType());

    if (filterContext.Exception != null)
    {
        log.Error("Unhandled exception: " + filterContext.Exception.Message +
            ". Stack trace: " + filterContext.Exception.StackTrace, 
            filterContext.Exception);
    }
}

Cela fonctionne très bien si une exception non gérée s'est produite lors d'une action du contrôleur.

En ce qui concerne les erreurs 404, j'ai une erreur personnalisée configurée dans mon web.config comme suit:

<customErrors mode="On">
    <error statusCode="404" redirect="~/page-not-found"/>
</customErrors>

Et dans l'action du contrôleur qui gère l'url "page non trouvée", je consigne l'url d'origine demandée:

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult PageNotFound()
{
    log.Warn("404 page not found - " + Utils.SafeString(Request.QueryString["aspxerrorpath"]));

    return View();
}

Et cela fonctionne aussi.

Le problème que j'ai est de savoir comment enregistrer les erreurs qui se trouvent sur les pages .aspx elles-mêmes. Disons que j'ai une erreur de compilation sur l'une des pages ou un code en ligne qui lèvera une exception:

<% ThisIsNotAValidFunction(); %>
<% throw new Exception("help!"); %>

Il semble que l'attribut HandleError redirige correctement cela vers ma page Error.aspx dans le dossier Shared, mais il n'est certainement pas intercepté par la méthode OnActionExecuted de mon BaseController. Je pensais que je pourrais peut-être mettre le code de journalisation sur la page Error.aspx elle-même, mais je ne suis pas sûr de savoir comment récupérer les informations d'erreur à ce niveau.

Kevin Pang
la source
+1 pour ELMAH. Voici un tutoriel ELMAH que j'ai écrit pour vous aider à démarrer. N'oubliez pas d'utiliser le package Elmah.MVC lorsque vous utilisez ASP.NET MVC, pour éviter les problèmes avec les pages d'erreur personnalisées, etc.
ThomasArdal
Il existe quelques produits qui enregistrent toutes les erreurs survenant dans les applications .NET. Ils ne sont pas aussi bas que ELMAH ou log4net, mais vous font gagner beaucoup de temps si vous essayez simplement de surveiller et de diagnostiquer les erreurs: Bugsnag et AirBrake sont deux de ceux que je sais faire .NET
Don P

Réponses:

103

J'envisagerais de simplifier votre application Web en branchant Elmah .

Vous ajoutez l'assembly Elmah à votre projet, puis configurez votre web.config. Il enregistrera ensuite les exceptions créées au niveau du contrôleur ou de la page. Il peut être configuré pour se connecter à différents endroits (comme SQL Server, Email, etc.). Il fournit également une interface Web, afin que vous puissiez parcourir le journal des exceptions.

C'est la première chose que j'ajoute à toute application mvc asp.net que je crée.

J'utilise toujours log4net, mais j'ai tendance à l'utiliser pour journaliser le débogage / info, et laisser toutes les exceptions à Elmah.

Vous pouvez également trouver plus d'informations dans la question Comment consignez-vous les erreurs (exceptions) dans vos applications ASP.NET? .

Andrew Rimmer
la source
3
J'ai commencé à utiliser Elmah récemment et c'est l'un des enregistreurs d'exceptions les plus simples et les plus simples que j'ai jamais utilisés. J'ai lu un article disant que MS devrait l'inclure dans ASP.net et je suis d'accord.
dtc
14
Pourquoi j'ai besoin d'ELMAH et de log4net pour l'application. enregistrement? Pourquoi pas une seule solution?
VJAI
Cela fonctionnera-t-il même si j'ai une architecture à n niveaux? Contrôleurs - services - référentiels?
a.farkas2508
2
ELMAH est surévalué.
Ronnie Overby le
ELMAH est-il gratuit?
Dallas
38

Vous pouvez vous connecter à l'événement OnError dans Global.asax.

Quelque chose comme ça:

/// <summary>
/// Handles the Error event of the Application control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
protected void Application_Error(object sender, EventArgs e)
{
    if (Server != null)
    {
        Exception ex = Server.GetLastError();

        if (Response.StatusCode != 404 )
        {
            Logging.Error("Caught in Global.asax", ex);
        }

    }


}
Chuck Conway
la source
3
Cela devrait attraper toutes les exceptions. Je considère que c'est la meilleure pratique.
Andrei Rînea
4
Selon l'analyse de valeur de ReSharper, Serversera toujours non nul.
Drew Noakes le
6
Ignorer 404 n'a pas fonctionné pour moi comme vous l'avez écrit. J'ai écritif (ex is HttpException && ((HttpException)ex).GetHttpCode() == 404) return;
pauloya
21

MVC3
Create Attribute qui hérite de HandleErrorInfoAttribute et inclut votre choix de journalisation

public class ErrorLoggerAttribute : HandleErrorAttribute 
{
    public override void OnException(ExceptionContext filterContext)
    {
        LogError(filterContext);
        base.OnException(filterContext);
    }

    public void LogError(ExceptionContext filterContext)
    {
       // You could use any logging approach here

        StringBuilder builder = new StringBuilder();
        builder
            .AppendLine("----------")
            .AppendLine(DateTime.Now.ToString())
            .AppendFormat("Source:\t{0}", filterContext.Exception.Source)
            .AppendLine()
            .AppendFormat("Target:\t{0}", filterContext.Exception.TargetSite)
            .AppendLine()
            .AppendFormat("Type:\t{0}", filterContext.Exception.GetType().Name)
            .AppendLine()
            .AppendFormat("Message:\t{0}", filterContext.Exception.Message)
            .AppendLine()
            .AppendFormat("Stack:\t{0}", filterContext.Exception.StackTrace)
            .AppendLine();

        string filePath = filterContext.HttpContext.Server.MapPath("~/App_Data/Error.log");

        using(StreamWriter writer = File.AppendText(filePath))
        {
            writer.Write(builder.ToString());
            writer.Flush();
        }
    }

Placer l'attribut dans Global.asax RegisterGlobalFilters

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
       // filters.Add(new HandleErrorAttribute());
        filters.Add(new ErrorLoggerAttribute());
    }
marque
la source
1

Avez-vous pensé à étendre l'attribut HandleError? En outre, Scott a un bon article de blog sur les intercepteurs de filtre sur les contrôleurs / actions ici .

Kieron
la source
1

La vue Error.aspx est définie comme ceci:

namespace MvcApplication1.Views.Shared
{
    public partial class Error : ViewPage<HandleErrorInfo>
    {
    }
}

Le HandleErrorInfo a trois propriétés: string ActionName string ControllerName Exception Exception

Vous devriez pouvoir accéder à HandleErrorInfo et donc à l'exception dans la vue.

Praveen Angyan
la source
0

Vous pouvez essayer d'examiner HttpContext.Error, mais je n'en suis pas sûr.

Mike Chaliy
la source