web-dev-qa-db-fra.com

Consignation des erreurs dans ASP.NET MVC

J'utilise actuellement log4net dans mon application ASP.NET MVC pour consigner les exceptions. Pour ce faire, tous mes contrôleurs héritent d'une classe BaseController. Dans l'événement OnActionExecuting de BaseController, je consigne toutes les exceptions susceptibles de 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 ceci:

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

Et dans l'action du contrôleur qui gère l'URL "page-not-found", 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 ça marche aussi.

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

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

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

106
Kevin Pang

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 (tels que SQL Server, Email, etc.). Il fournit également une interface Web, qui vous permet de parcourir le journal des exceptions.

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

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

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

99
Andrew Rimmer

Vous pouvez accéder à l'événement OnError dans le fichier 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);
        }

    }


}
36
Chuck Conway

MVC3
Créer un attribut héritant de HandleErrorInfoAttribute et comprenant 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();
        }
    }

Attribut Place dans Global.asax RegisterGlobalFilters

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
       // filters.Add(new HandleErrorAttribute());
        filters.Add(new ErrorLoggerAttribute());
    }
21
Mark

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

1
Kieron

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

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

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.

1
Praveen Angyan

Vous pouvez essayer d'examiner HttpContext.Error, mais je ne suis pas sûr à ce sujet.

0
Mike Chaliy