Erros de registro em ASP.NET MVC

109

Atualmente, estou usando log4net em meu aplicativo ASP.NET MVC para registrar exceções. Estou fazendo isso fazendo com que todos os meus controladores herdem de uma classe BaseController. No evento OnActionExecuting do BaseController, registro todas as exceções que possam ter ocorrido:

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

Isso funciona muito bem se uma exceção não tratada ocorreu durante uma ação do controlador.

Quanto aos erros 404, eu tenho um erro personalizado configurado em meu web.config assim:

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

E na ação do controlador que lida com o url "página não encontrada", eu registro o URL original que está sendo solicitado:

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

    return View();
}

E isso também funciona.

O problema que estou tendo é como registrar os erros que estão nas próprias páginas .aspx. Digamos que eu tenha um erro de compilação em uma das páginas ou algum código embutido que gerará uma exceção:

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

Parece que o atributo HandleError está redirecionando isso corretamente para minha página Error.aspx na pasta Compartilhada, mas definitivamente não está sendo capturado pelo método OnActionExecuted do meu BaseController. Eu estava pensando que talvez pudesse colocar o código de registro na própria página Error.aspx, mas não tenho certeza de como recuperar as informações de erro nesse nível.

Kevin Pang
fonte
+1 para ELMAH. Aqui está um Tutorial ELMAH que escrevi para ajudá-lo a começar. Lembre-se também de usar o pacote Elmah.MVC ao usar ASP.NET MVC, para evitar problemas com páginas de erro personalizadas, etc.
ThomasArdal
Existem alguns produtos por aí que registram todos os erros que ocorrem em aplicativos .NET. Eles não são de baixo nível como ELMAH ou log4net, mas economizam muito tempo se você está apenas tentando monitorar e diagnosticar erros: Bugsnag e AirBrake são dois dos que eu sei fazer. NET
Don P

Respostas:

103

Eu consideraria simplificar seu aplicativo da web conectando Elmah .

Você adiciona o assembly Elmah ao seu projeto e, em seguida, configura seu web.config. Em seguida, ele registrará as exceções criadas no controlador ou no nível da página. Ele pode ser configurado para fazer logon em vários lugares diferentes (como SQL Server, Email, etc.). Ele também fornece um frontend da Web, para que você possa navegar pelo log de exceções.

É a primeira coisa que adiciono a qualquer aplicativo mvc asp.net que eu criar.

Ainda uso o log4net, mas tendo a usá-lo para registrar depuração / informações e deixar todas as exceções para Elmah.

Você também pode encontrar mais informações na pergunta Como você registra erros (exceções) em seus aplicativos ASP.NET? .

Andrew Rimmer
fonte
3
Comecei a usar o Elmah recentemente e é um dos registradores de exceção mais inteligentes e simples que já usei. Eu li um post dizendo que a MS deveria incluí-lo no ASP.net e eu concordo.
dtc de
14
Por que preciso de ELMAH e log4net para o aplicativo. exploração madeireira? Por que não uma solução única?
VJAI de
Isso funcionará mesmo se eu tiver uma arquitetura de n camadas? Controladores - serviços - repositórios?
a.farkas2508
2
ELMAH está superestimado.
Ronnie Overby,
O ELMAH é gratuito?
Dallas
38

Você pode conectar-se ao evento OnError no Global.asax.

Algo assim:

/// <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
fonte
3
Isso deve capturar todas as exceções. Eu considero esta a melhor prática.
Andrei Rînea
4
De acordo com a análise de valor do ReSharper, Serversempre será não nulo.
Drew Noakes em
6
Ignorar 404 não funcionou para mim da maneira que você escreveu. Eu escreviif (ex is HttpException && ((HttpException)ex).GetHttpCode() == 404) return;
pauloya
21

MVC3
Criar Atributo que herda de HandleErrorInfoAttribute e inclui sua escolha 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 o atributo em Global.asax RegisterGlobalFilters

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

Você já pensou em estender o atributo HandleError? Além disso, Scott tem uma boa postagem no blog sobre interceptores de filtro em controladores / ações aqui .

Kieron
fonte
1

A visualização Error.aspx é definida assim:

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

O HandleErrorInfo tem três propriedades: string ActionName string ControllerName Exception Exception

Você deve ser capaz de acessar HandleErrorInfo e, portanto, a Exception dentro da visualização.

Praveen Angyan
fonte
0

Você pode tentar examinar HttpContext.Error, mas não tenho certeza sobre isso.

Mike Chaliy
fonte