Rejestrowanie błędów w ASP.NET MVC

109

Obecnie używam log4net w mojej aplikacji ASP.NET MVC do rejestrowania wyjątków. Sposób, w jaki to robię, polega na tym, że wszystkie moje kontrolery dziedziczą z klasy BaseController. W zdarzeniu OnActionExecuting BaseControllera rejestruję wszelkie wyjątki, które mogły wystąpić:

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

Działa to świetnie, jeśli wystąpił nieobsługiwany wyjątek podczas akcji kontrolera.

Jeśli chodzi o błędy 404, mam niestandardowy błąd ustawiony w moim pliku web.config w następujący sposób:

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

W akcji kontrolera, która obsługuje adres URL „nie znaleziono strony”, rejestruję żądany pierwotny adres URL:

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

    return View();
}

I to też działa.

Problem, który mam, polega na tym, jak rejestrować błędy na samych stronach .aspx. Załóżmy, że mam błąd kompilacji na jednej ze stron lub jakiś wbudowany kod, który zgłosi wyjątek:

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

Wygląda na to, że atrybut HandleError prawidłowo przekierowuje to na moją stronę Error.aspx w folderze udostępnionym, ale zdecydowanie nie jest przechwytywany przez metodę OnActionExecuted mojego BaseControllera. Myślałem, że mógłbym umieścić kod logowania na samej stronie Error.aspx, ale nie jestem pewien, jak pobrać informacje o błędzie na tym poziomie.

Kevin Pang
źródło
+1 dla ELMAH. Oto samouczek ELMAH, który napisałem, aby pomóc Ci zacząć. Pamiętaj również, aby używać pakietu Elmah.MVC podczas korzystania z ASP.NET MVC, aby uniknąć problemów z niestandardowymi stronami błędów itp.
ThomasArdal
Istnieje kilka produktów, które będą rejestrować wszystkie błędy występujące w aplikacjach .NET. Nie są tak niskopoziomowe jak ELMAH czy log4net, ale oszczędzają mnóstwo czasu, jeśli próbujesz tylko monitorować i diagnozować błędy: Bugsnag i AirBrake to dwa z nich, które znam. NET
Don P

Odpowiedzi:

103

Rozważyłbym uproszczenie Twojej aplikacji internetowej poprzez podłączenie Elmaha .

Dodaj zestaw Elmah do projektu, a następnie skonfiguruj plik web.config. Następnie zarejestruje wyjątki utworzone na poziomie kontrolera lub strony. Można go skonfigurować tak, aby logował się do różnych miejsc (takich jak SQL Server, poczta e-mail itp.). Zapewnia również interfejs WWW, dzięki czemu można przeglądać dziennik wyjątków.

Jest to pierwsza rzecz, którą dodaję do każdej utworzonej przeze mnie aplikacji asp.net mvc.

Nadal używam log4net, ale zwykle używam go do logowania debugowania / informacji i zostawiam wszystkie wyjątki Elmah.

Więcej informacji można również znaleźć w pytaniu Jak rejestrować błędy (wyjątki) w aplikacjach ASP.NET? .

Andrew Rimmer
źródło
3
Niedawno zacząłem używać Elmah i jest to jeden z najsprytniejszych i najprostszych rejestratorów wyjątków, jakiego kiedykolwiek używałem. Przeczytałem post mówiący, że MS powinien umieścić to w ASP.net i zgadzam się.
dtc
14
Dlaczego potrzebuję zarówno ELMAH, jak i log4net dla aplikacji. Logowanie? Dlaczego nie jedno rozwiązanie?
VJAI
Czy to zadziała, nawet jeśli mam architekturę n-warstwową? Kontrolery - usługi - repozytoria?
a.farkas2508
2
ELMAH jest zawyżony.
Ronnie Overby
Czy ELMAH jest za darmo?
Dallas
38

Możesz podłączyć się do zdarzenia OnError w pliku Global.asax.

Coś takiego:

/// <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
źródło
3
Powinno to obejmować wszystkie wyjątki. Uważam to za najlepszą praktykę.
Andrei Rînea
4
Zgodnie z analizą wartości ReSharper, Serverzawsze będzie różna od null.
Drew Noakes
6
Ignorowanie 404 nie działało dla mnie tak, jak to napisałeś. Napisałemif (ex is HttpException && ((HttpException)ex).GetHttpCode() == 404) return;
pauloya
21

MVC3
Create Attribute, który dziedziczy po HandleErrorInfoAttribute i obejmuje wybór logowania

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

Umieść atrybut w Global.asax RegisterGlobalFilters

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
       // filters.Add(new HandleErrorAttribute());
        filters.Add(new ErrorLoggerAttribute());
    }
znak
źródło
1

Czy myślałeś o rozszerzeniu atrybutu HandleError? Również Scott ma dobrą blogu o przechwytujących filtracyjnych na kontrolerach / działań tutaj .

Kieron
źródło
1

Widok Error.aspx jest zdefiniowany w następujący sposób:

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

HandleErrorInfo ma trzy właściwości: string ActionName string ControllerName Exception Exception

Powinieneś mieć dostęp do HandleErrorInfo, a tym samym do wyjątku w widoku.

Praveen Angyan
źródło
0

Możesz spróbować zbadać HttpContext.Error, ale nie jestem tego pewien.

Mike Chaliy
źródło