在 ASP.NET MVC 中记录错误

Posted

技术标签:

【中文标题】在 ASP.NET MVC 中记录错误【英文标题】:Logging errors in ASP.NET MVC 【发布时间】:2010-10-08 19:53:13 【问题描述】:

我目前在我的 ASP.NET MVC 应用程序中使用 log4net 来记录异常。我这样做的方式是让我的所有控制器都继承自 BaseController 类。在 BaseController 的 OnActionExecuting 事件中,我记录了所有可能发生的异常:

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

如果在控制器操作期间发生未处理的异常,这将非常有用。

至于 404 错误,我在 web.config 中设置了一个自定义错误,如下所示:

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

在处理“page-not-found”网址的控制器操作中,我记录了请求的原始网址:

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult PageNotFound()

    log.Warn("404 page not found - " + Utils.SafeString(Request.QueryString["aspxerrorpath"]));

    return View();

这也有效。

我遇到的问题是如何记录 .aspx 页面本身上的错误。假设我在某个页面或某些内联代码上出现编译错误,会引发异常:

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

HandleError 属性似乎正确地将其重新路由到 Shared 文件夹中的 Error.aspx 页面,但它肯定没有被我的 BaseController 的 OnActionExecuted 方法捕获。我在想我可以将日志记录代码放在 Error.aspx 页面本身上,但我不确定如何在该级别检索错误信息。

【问题讨论】:

ELMAH +1。这是我写的ELMAH Tutorial,以帮助您入门。还记得在使用 ASP.NET MVC 时使用 Elmah.MVC 包,以避免自定义错误页面等问题。 有一些产品可以记录 .NET 应用程序中发生的所有错误。它们不像 ELMAH 或 log4net 那样低级,但如果您只是想监控和诊断错误,可以为您节省大量时间:Bugsnag 和 AirBrake 是我知道的 .NET 中的两个跨度> 【参考方案1】:

我会考虑通过插入 Elmah 来简化您的 Web 应用程序。

您将 Elmah 程序集添加到您的项目中,然后配置您的 web.config。然后它将记录在控制器或页面级别创建的异常。它可以配置为登录到各种不同的地方(如 SQL Server、电子邮件等)。它还提供了一个 Web 前端,以便您可以浏览异常日志。

这是我添加到我创建的任何 asp.net mvc 应用程序中的第一件事。

我仍然使用 log4net,但我倾向于使用它来记录调试/信息,并将所有异常留给 Elmah。

您还可以在问题How do you log errors (Exceptions) in your ASP.NET apps?中找到更多信息。

【讨论】:

我最近开始使用 Elmah,它是我用过的最流畅、最简单的异常记录器之一。我读到一篇文章说 MS 应该将其包含在 ASP.net 中,我同意。 为什么我需要 ELMAH 和 log4net 的应用程序。记录?为什么不是单一的解决方案? 即使我有 n 层架构,这也能工作吗?控制器 - 服务 - 存储库? ELMAH 是免费的吗? @Dallas,是的,它在 NuGet 上。【参考方案2】:

您可以在 Global.asax 中挂钩 OnError 事件。

类似这样的:

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

    



【讨论】:

这应该捕获所有异常。我认为这是最佳做法。 根据 ReSharper 的价值分析,Server 将始终为非空。 忽略 404 并没有像你写的那样对我有用。我写了if (ex is HttpException &amp;&amp; ((HttpException)ex).GetHttpCode() == 404) return;【参考方案3】:

MVC3 创建继承自 HandleErrorInfoAttribute 并包含您选择的日志记录的属性

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:\t0", filterContext.Exception.Source)
            .AppendLine()
            .AppendFormat("Target:\t0", filterContext.Exception.TargetSite)
            .AppendLine()
            .AppendFormat("Type:\t0", filterContext.Exception.GetType().Name)
            .AppendLine()
            .AppendFormat("Message:\t0", filterContext.Exception.Message)
            .AppendLine()
            .AppendFormat("Stack:\t0", 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();
        
    

在 Global.asax RegisterGlobalFilters 中放置属性

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

【讨论】:

标记...在那个 LogError 方法中,我想调用一个 api 来发送发生错误的警报电子邮件。但是,我必须更改签名以包含它不喜欢的同步。关于如何使用您提到的 HandleErrorInfoAttribute 方法调用 web api 的任何解决方案。【参考方案4】:

您是否考虑过扩展 HandleError 属性?此外,Scott 有一篇关于控制器/操作上的过滤器拦截器的精彩博客文章 here。

【讨论】:

【参考方案5】:

Error.aspx 视图的定义如下:

namespace MvcApplication1.Views.Shared

    public partial class Error : ViewPage<HandleErrorInfo>
    
    

HandleErrorInfo 具有三个属性: 字符串动作名称 字符串控制器名称 异常异常

您应该能够访问 HandleErrorInfo 以及视图中的异常。

【讨论】:

【参考方案6】:

您可以尝试检查 HttpContext.Error,但我不确定。

【讨论】:

以上是关于在 ASP.NET MVC 中记录错误的主要内容,如果未能解决你的问题,请参考以下文章

在 ASP.NET Core 2 中处理异常的推荐方法? [关闭]

表单身份验证、ASP.NET MVC 和 WCF RESTful 服务

MVC Contrib 是不是兑现了提高 ASP.NET MVC 生产力的承诺

处理 OAuth 2.0 身份验证 - 在 ASP.NET MVC 应用程序中获取令牌重定向令牌响应

在 ASP.NET MVC 中记录错误

未记录 ELMAH 和 ASP.NET MVC 自定义错误