Errores de registro en ASP.NET MVC

109

Actualmente estoy usando log4net en mi aplicación ASP.NET MVC para registrar excepciones. La forma en que estoy haciendo esto es haciendo que todos mis controladores hereden de una clase BaseController. En el evento OnActionExecuting de BaseController, registro cualquier excepción que pueda haber ocurrido:

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

Esto funciona muy bien si se produjo una excepción no controlada durante una acción del controlador.

En cuanto a los errores 404, tengo un error personalizado configurado en mi web.config así:

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

Y en la acción del controlador que maneja la URL "página no encontrada", registro la URL original que se solicita:

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

    return View();
}

Y esto también funciona.

El problema que tengo es cómo registrar los errores que se encuentran en las páginas .aspx. Digamos que tengo un error de compilación en una de las páginas o algún código en línea que generará una excepción:

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

Parece que el atributo HandleError está redireccionando correctamente esto a mi página Error.aspx en la carpeta compartida, pero definitivamente no está siendo detectado por el método OnActionExecuted de mi BaseController. Estaba pensando que tal vez podría poner el código de registro en la página Error.aspx, pero no estoy seguro de cómo recuperar la información de error en ese nivel.

Kevin Pang
fuente
+1 para ELMAH. Aquí hay un tutorial de ELMAH que escribí para ayudarlo a comenzar. También recuerde usar el paquete Elmah.MVC cuando use ASP.NET MVC, para evitar problemas con páginas de error personalizadas, etc.
ThomasArdal
Hay algunos productos que registrarán todos los errores que se produzcan en las aplicaciones .NET. No son de tan bajo nivel como ELMAH o log4net, pero le ahorran mucho tiempo si solo está tratando de monitorear y diagnosticar errores: Bugsnag y AirBrake son dos de los que sé que hacen .NET
Don P

Respuestas:

103

Consideraría simplificar su aplicación web conectando Elmah .

Agrega el ensamblado Elmah a su proyecto y luego configura su web.config. Luego, registrará las excepciones creadas a nivel de controlador o página. Se puede configurar para iniciar sesión en varios lugares diferentes (como SQL Server, correo electrónico, etc.). También proporciona una interfaz web, para que pueda navegar por el registro de excepciones.

Es lo primero que agrego a cualquier aplicación asp.net mvc que cree.

Todavía uso log4net, pero tiendo a usarlo para registrar información / depuración, y dejo todas las excepciones a Elmah.

También puede encontrar más información en la pregunta ¿Cómo registra errores (excepciones) en sus aplicaciones ASP.NET? .

Andrew Rimmer
fuente
3
Comencé a usar Elmah recientemente y es uno de los registradores de excepciones más hábiles y simples que he usado. Leí una publicación que decía que MS debería incluirlo en ASP.net y estoy de acuerdo.
dtc
14
Por qué necesito tanto ELMAH como log4net para la aplicación. ¿Inicio sesión? ¿Por qué no una única solución?
VJAI
¿Funcionará esto incluso si tengo una arquitectura de n niveles? Controladores - servicios - repositorios?
a.farkas2508
2
ELMAH está sobrevalorado.
Ronnie Overby
¿ELMAH es gratis?
Dallas
38

Puede conectarse al evento OnError en Global.asax.

Algo como esto:

/// <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
fuente
3
Esto debería detectar todas las excepciones. Considero que esta es la mejor práctica.
Andrei Rînea
4
Según el análisis de valor de ReSharper, Serversiempre será no nulo.
Drew Noakes
6
Ignorar el 404 no funcionó para mí de la forma en que lo escribió. Escribíif (ex is HttpException && ((HttpException)ex).GetHttpCode() == 404) return;
pauloya
21

MVC3
Create Attribute que hereda de HandleErrorInfoAttribute e incluye su elección de registro

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

Coloque el atributo en Global.asax RegisterGlobalFilters

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
       // filters.Add(new HandleErrorAttribute());
        filters.Add(new ErrorLoggerAttribute());
    }
marca
fuente
1

¿Ha pensado en ampliar el atributo HandleError? Además, Scott tiene una buena publicación de blog sobre interceptores de filtros en controladores / acciones aquí .

Kieron
fuente
1

La vista Error.aspx se define así:

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

HandleErrorInfo tiene tres propiedades: string ActionName string ControllerName Exception Exception

Debería poder acceder a HandleErrorInfo y, por lo tanto, a la excepción dentro de la vista.

Praveen Angyan
fuente
0

Puede intentar examinar HttpContext.Error, pero no estoy seguro de esto.

Mike Chaliy
fuente