ChatGPT解决这个技术问题 Extra ChatGPT

Logging errors in ASP.NET MVC

I'm currently using log4net in my ASP.NET MVC application to log exceptions. The way I'm doing this is by having all my controllers inherit from a BaseController class. In the BaseController's OnActionExecuting event, I log any exceptions that may have occurred:

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

This works great if an unhandled exception occurred during a controller action.

As for 404 errors, I have a custom error set up in my web.config like so:

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

And in the controller action that handles the "page-not-found" url, I log the original url being requested:

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

    return View();
}

And this also works.

The problem that I'm having is how to log errors that are on the .aspx pages themselves. Let's say I have a compilation error on one of the pages or some inline code that will throw an exception:

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

It appears that the HandleError attribute is correctly rerouting this to my Error.aspx page in the Shared folder, but it is definitely not being caught by my BaseController's OnActionExecuted method. I was thinking I could maybe put the logging code on the Error.aspx page itself, but I'm unsure of how to retrieve the error information at that level.

+1 for ELMAH. Here's an ELMAH Tutorial I wrote to help you get started. Also remember to use the Elmah.MVC package when using ASP.NET MVC, to avoid problems with custom error pages etc.
Theres a few products out there that will log all errors occurring in .NET apps. They're not as low-level as ELMAH or log4net, but save you a ton of time if you're just trying to monitor & diagnose errors: Bugsnag and AirBrake are two of that I know do .NET

C
Community

I would consider simplifying your web application by plugging in Elmah.

You add the Elmah assembly to your project and then configure your web.config. It will then log exceptions created at controller or page level. It can be configured to log to various different places (like SQL Server, Email etc). It also provides a web frontend, so that you can browse through the log of exceptions.

Its the first thing I add to any asp.net mvc app I create.

I still use log4net, but I tend to use it for logging debug/info, and leave all exceptions to Elmah.

You can also find more information in the question How do you log errors (Exceptions) in your ASP.NET apps?.


I started using Elmah recently and it's one of the slickest and simplest exception loggers I've ever used. I read a post saying that MS should include it in ASP.net and I agree.
Why I need both ELMAH and log4net for app. logging? Why not a single solution?
Will this work even if I have n-tier architecture? Controllers - services - repositories?
Is ELMAH for free?
@Dallas, yes, it's on NuGet.
C
Chuck Conway

You can hook into the OnError event in the Global.asax.

Something like this:

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

    }


}

This should catch all exceptions. I consider this the best practice.
According to ReSharper's value analysis, Server will always be non-null.
Ignoring 404 didn't work for me the way you wrote it. I wrote if (ex is HttpException && ((HttpException)ex).GetHttpCode() == 404) return;
M
Mark

MVC3 Create Attribute that inherits from HandleErrorInfoAttribute and includes your choice of logging

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

Place attribute in Global.asax RegisterGlobalFilters

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

Mark...in that LogError method I want to call an api to send an alert email that an error occurred. However, I have to change the signature to contain an sync an it does not like that. Any solution on how to call the web api with this HandleErrorInfoAttribute approach you mention.
K
Kieron

Have you thought about extending the HandleError attribute? Also, Scott has a good blog post about filter interceptors on controllers/ actions here.


P
Praveen Angyan

The Error.aspx view is defined like this:

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

The HandleErrorInfo has three properties: string ActionName string ControllerName Exception Exception

You should be able to access HandleErrorInfo and therefore the Exception within the view.


M
Mike Chaliy

You can try to examine HttpContext.Error, but I am not sure on this.