【问题标题】:How to get ELMAH to work with ASP.NET MVC [HandleError] attribute?如何让 ELMAH 与 ASP.NET MVC [HandleError] 属性一起使用?
【发布时间】:2010-10-20 11:28:39
【问题描述】:

我正在尝试使用 ELMAH 在我的 ASP.NET MVC 应用程序中记录错误,但是当我在控制器上使用 [HandleError] 属性时,ELMAH 不会在错误发生时记录任何错误。

我猜是因为 ELMAH 只记录未处理的错误,而 [HandleError] 属性正在处理错误,因此无需记录。

我该如何修改或如何修改属性,以便 ELMAH 知道存在错误并记录它..

编辑:让我确保每个人都明白,我知道我可以修改属性,这不是我要问的问题...使用 handleerror 属性时,ELMAH 被绕过,这意味着它不会看到有一个错误,因为它已经被属性处理了......我要问的是有没有办法让 ELMAH 看到错误并记录它,即使属性处理了它......我四处搜索并没有查看任何要调用的方法以强制它记录错误....

【问题讨论】:

  • 哇,我希望 Jeff 或 Jared 能回答这个问题。他们将 ELMAH 用于 Stackoverflow ;)
  • 嗯,奇怪 - 我们不使用 HandleErrorAttribute - Elmah 是在我们的 web.config 的 部分中设置的。使用 HandleErrorAttribute 有什么好处吗?
  • @Jarrod - 很高兴看到你的 ELMAH 前叉有什么“自定义”。
  • @dswatik 您还可以通过在 web.config 中将 redirectMode 设置为 ResponseRewrite 来防止重定向。见blog.turlov.com/2009/01/…
  • 我一直遇到网络文档和帖子,谈论 [HandleError] 属性和 Elmah,但我没有看到这解决的行为(例如 Elmah 没有记录“已处理”错误),当我设置时假案。这是因为从 Elmah.MVC 2.0.x 开始,不再需要此自定义 HandleErrorAttribute;它包含在 nuget 包中。

标签: asp.net-mvc logging elmah


【解决方案1】:

您可以将HandleErrorAttribute 子类化并覆盖其OnException 成员(无需复制),以便它使用ELMAH 记录异常,并且仅当基本实现处理它时。您需要的最少代码如下:

using System.Web.Mvc;
using Elmah;

public class HandleErrorAttribute : System.Web.Mvc.HandleErrorAttribute
{
    public override void OnException(ExceptionContext context)
    {
        base.OnException(context);
        if (!context.ExceptionHandled) 
            return;
        var httpContext = context.HttpContext.ApplicationInstance.Context;
        var signal = ErrorSignal.FromContext(httpContext);
        signal.Raise(context.Exception, httpContext);
    }
}

首先调用基本实现,使其有机会将异常标记为正在处理。只有这样才会发出异常信号。上面的代码很简单,如果在HttpContext可能不可用的环境中使用可能会导致问题,例如测试。因此,您将需要更具防御性的代码(代价是稍长一些):

using System.Web;
using System.Web.Mvc;
using Elmah;

public class HandleErrorAttribute : System.Web.Mvc.HandleErrorAttribute
{
    public override void OnException(ExceptionContext context)
    {
        base.OnException(context);
        if (!context.ExceptionHandled       // if unhandled, will be logged anyhow
            || TryRaiseErrorSignal(context) // prefer signaling, if possible
            || IsFiltered(context))         // filtered?
            return;

        LogException(context);
    }

    private static bool TryRaiseErrorSignal(ExceptionContext context)
    {
        var httpContext = GetHttpContextImpl(context.HttpContext);
        if (httpContext == null)
            return false;
        var signal = ErrorSignal.FromContext(httpContext);
        if (signal == null)
            return false;
        signal.Raise(context.Exception, httpContext);
        return true;
    }

    private static bool IsFiltered(ExceptionContext context)
    {
        var config = context.HttpContext.GetSection("elmah/errorFilter")
                        as ErrorFilterConfiguration;

        if (config == null)
            return false;

        var testContext = new ErrorFilterModule.AssertionHelperContext(
                              context.Exception, 
                              GetHttpContextImpl(context.HttpContext));
        return config.Assertion.Test(testContext);
    }

    private static void LogException(ExceptionContext context)
    {
        var httpContext = GetHttpContextImpl(context.HttpContext);
        var error = new Error(context.Exception, httpContext);
        ErrorLog.GetDefault(httpContext).Log(error);
    }

    private static HttpContext GetHttpContextImpl(HttpContextBase context)
    {
        return context.ApplicationInstance.Context;
    }
}

第二个版本将首先尝试使用来自 ELMAH 的error signaling,这涉及到完全配置的管道,如日志记录、邮件、过滤等等。如果做不到这一点,它会尝试查看是否应该过滤错误。如果不是,则简单地记录错误。此实现不处理邮件通知。如果可以发出异常信号,则如果配置为发送邮件,则会发送邮件。

您可能还需要注意,如果多个HandleErrorAttribute 实例生效,则不会发生重复日志记录,但以上两个示例应该可以帮助您入门。

【讨论】:

  • 优秀。我根本没有尝试实施 Elmah。我只是想以一种适用于 MVC 的方式连接我自己多年来使用的错误报告。你的代码给了我一个起点。 +1
  • 您不需要继承 HandleErrorAttribute。您可以简单地拥有一个 IExceptionFilter 实现并将其与 HandleErrorAttribute 一起注册。另外,我不明白为什么在 ErrorSignal.Raise(..) 失败时需要回退。如果管道配置不当,则应进行修复。对于 5 行 IExceptionFilter 检查点 4. 这里 - ivanz.com/2011/05/08/…
  • 请您对@IvanZlatev 下面关于适用性、缺点等的答案发表评论。人们评论说它更容易/更短/更简单,并且与您的答案相同,因此应该是标记为正确答案。最好有您对此的看法并通过这些答案获得一些清晰。
  • 这仍然相关还是 ELMAH.MVC 处理这个?
  • 我什至想知道它在今天的版本中是否仍然相关
【解决方案2】:

您可以通过引入将 HandleErrorWithElmah 属性注入每个控制器的自定义控制器工厂来更进一步地使用上面的代码。

有关更多信息,请查看我关于登录 MVC 的博客系列。第一篇文章介绍了如何为 MVC 设置和运行 Elmah。

文末有可下载代码的链接。希望对您有所帮助。

http://dotnetdarren.wordpress.com/

【讨论】:

  • 在我看来,将它粘贴在基本控制器类上会容易得多!
  • Darren 上面关于日志记录和异常处理的系列非常值得一读!!!非常彻底!
【解决方案3】:

这正是我的 MVC 站点配置所需要的!

按照 Atif Aziz 的建议,我对 OnException 方法进行了一些修改以处理多个 HandleErrorAttribute 实例:

请记住,您可能需要注意,如果多个 HandleErrorAttribute 实例有效,则不会发生重复日志记录。

我只是在调用基类之前检查context.ExceptionHandled,只是为了知道其他人是否在当前处理程序之前处理了异常。
它对我有用,我发布代码以防其他人需要它,并询问是否有人知道我是否忽略了任何东西。

希望有用:

public override void OnException(ExceptionContext context)
{
    bool exceptionHandledByPreviousHandler = context.ExceptionHandled;

    base.OnException(context);

    Exception e = context.Exception;
    if (exceptionHandledByPreviousHandler
        || !context.ExceptionHandled  // if unhandled, will be logged anyhow
        || RaiseErrorSignal(e)        // prefer signaling, if possible
        || IsFiltered(context))       // filtered?
        return;

    LogException(e);
}

【讨论】:

  • 您似乎没有围绕调用 base.OnException()... 的“if”语句。和 (exceptionHandledByPreviousHandler || !context.ExceptionHandled || ...) 相互抵消并且永远是真的。我错过了什么吗?
  • 首先我检查是否有任何其他在当前调用之前调用的处理程序管理异常并将结果存储在变量中:exceptionHandlerdByPreviousHandler。然后我给当前处理程序管理异常本身的机会:base.OnException(context).
  • 首先我检查是否有任何其他在当前调用之前调用的处理程序管理异常并将结果存储在变量中:exceptionHandlerdByPreviousHandler。然后我给当前处理程序管理异常本身的机会:base.OnException(context)。如果异常以前没有被管理过,它可以是: 1 - 它由当前处理程序管理,然后:exceptionHandledByPreviousHandler = false 和 !context.ExceptionHandled = false 2 - 它不由当前处理程序管理并且:exceptionHandledByPreviousHandler = false 和 !context。异常处理为真。只有案例 1 会记录。
【解决方案4】:

对我来说,让电子邮件记录正常工作非常重要。一段时间后,我发现这在 Atif 示例中只需要多两行代码。

public class HandleErrorWithElmahAttribute : HandleErrorAttribute
{
    static ElmahMVCMailModule error_mail_log = new ElmahMVCMailModule();

    public override void OnException(ExceptionContext context)
    {
        error_mail_log.Init(HttpContext.Current.ApplicationInstance);
        [...]
    }
    [...]
}

我希望这会对某人有所帮助:)

【讨论】:

    【解决方案5】:

    我是 ASP.NET MVC 的新手。我遇到了同样的问题,以下是我在 Erorr.vbhtml 中的可行方法(如果您只需要使用 Elmah 日志记录错误,它就可以工作)

    @ModelType System.Web.Mvc.HandleErrorInfo
    
        @Code
            ViewData("Title") = "Error"
            Dim item As HandleErrorInfo = CType(Model, HandleErrorInfo)
            //To log error with Elmah
            Elmah.ErrorLog.GetDefault(HttpContext.Current).Log(New Elmah.Error(Model.Exception, HttpContext.Current))
        End Code
    
    <h2>
        Sorry, an error occurred while processing your request.<br />
    
        @item.ActionName<br />
        @item.ControllerName<br />
        @item.Exception.Message
    </h2> 
    

    简直了!

    【讨论】:

    • 这是迄今为止最简单的解决方案。无需编写或注册自定义处理程序和东西。适合我
    • 对于任何 JSON / 非 HTML 响应将被忽略。
    • 这也是在视图中执行服务级别功能。不属于这里。
    【解决方案6】:

    对不起,但我认为接受的答案是矫枉过正。您需要做的就是:

    public class ElmahHandledErrorLoggerFilter : IExceptionFilter
    {
        public void OnException (ExceptionContext context)
        {
            // Log only handled exceptions, because all other will be caught by ELMAH anyway.
            if (context.ExceptionHandled)
                ErrorSignal.FromCurrentContext().Raise(context.Exception);
        }
    }
    

    然后在 Global.asax.cs 中注册(顺序很重要):

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

    【讨论】:

    • +1 非常好,无需扩展HandleErrorAttribute,无需覆盖BaseController 上的OnException。这是假设接受的答案。
    • @bigb 我认为您必须将异常包装在您自己的异常类型中以将内容附加到异常消息等(例如 new UnhandledLoggedException(Exception thrown) 在返回之前将某些内容附加到 Message .
    • Atif Aziz 创建了 ELMAH,我会接受他的回答
    • @jamiebarrow 我没有意识到这一点,但他的回答已经有大约 2 年的历史了,并且可能 API 已经过简化,以更短、更独立的方式支持问题的用例。
    • @Ivan Zlatev 真的无法开始工作ElmahHandledErrorLoggerFilter() elmah 只是记录未处理的错误,但未处理。我按照你提到的正确顺序注册了过滤器,有什么想法吗?
    【解决方案7】:

    现在 NuGet 中有一个 ELMAH.MVC 包,其中包括由 Atif 改进的解决方案,以及在 MVC 路由中处理 elmah 接口的控制器(不再需要使用该 axd)
    该解决方案(以及此处的所有解决方案)的问题在于,elmah 错误处理程序实际上以一种或另一种方式处理错误,而忽略了您可能希望设置为 customError 标记或通过 ErrorHandler 或您自己的错误处理程序 恕我直言,最好的解决方案是创建一个过滤器,它将在所有其他过滤器的末尾起作用并记录已经处理的事件。 elmah 模块应该负责记录应用程序未处理的其他错误。这也将允许您使用运行状况监视器和所有其他可以添加到 asp.net 的模块来查看错误事件

    我用反射器在 elmah.mvc 中的 ErrorHandler 上写了这篇文章

    public class ElmahMVCErrorFilter : IExceptionFilter
    {
       private static ErrorFilterConfiguration _config;
    
       public void OnException(ExceptionContext context)
       {
           if (context.ExceptionHandled) //The unhandled ones will be picked by the elmah module
           {
               var e = context.Exception;
               var context2 = context.HttpContext.ApplicationInstance.Context;
               //TODO: Add additional variables to context.HttpContext.Request.ServerVariables for both handled and unhandled exceptions
               if ((context2 == null) || (!_RaiseErrorSignal(e, context2) && !_IsFiltered(e, context2)))
               {
                _LogException(e, context2);
               }
           }
       }
    
       private static bool _IsFiltered(System.Exception e, System.Web.HttpContext context)
       {
           if (_config == null)
           {
               _config = (context.GetSection("elmah/errorFilter") as ErrorFilterConfiguration) ?? new ErrorFilterConfiguration();
           }
           var context2 = new ErrorFilterModule.AssertionHelperContext((System.Exception)e, context);
           return _config.Assertion.Test(context2);
       }
    
       private static void _LogException(System.Exception e, System.Web.HttpContext context)
       {
           ErrorLog.GetDefault((System.Web.HttpContext)context).Log(new Elmah.Error((System.Exception)e, (System.Web.HttpContext)context));
       }
    
    
       private static bool _RaiseErrorSignal(System.Exception e, System.Web.HttpContext context)
       {
           var signal = ErrorSignal.FromContext((System.Web.HttpContext)context);
           if (signal == null)
           {
               return false;
           }
           signal.Raise((System.Exception)e, (System.Web.HttpContext)context);
           return true;
       }
    }
    

    现在,在您的过滤器配置中,您想要执行以下操作:

        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            //These filters should go at the end of the pipeline, add all error handlers before
            filters.Add(new ElmahMVCErrorFilter());
        }
    

    请注意,我在那里留下了一条评论以提醒人们,如果他们想要添加一个将实际处理异常的全局过滤器,它应该在最后一个过滤器之前进行,否则您会遇到未处理的异常将被忽略的情况ElmahMVCErrorFilter 因为它没有被处理,应该由 Elmah 模块记录,但是下一个过滤器将异常标记为已处理并且模块忽略它,导致异常永远不会进入 elmah。

    现在,确保你的 webconfig 中 elmah 的 appsettings 看起来像这样:

    <add key="elmah.mvc.disableHandler" value="false" /> <!-- This handles elmah controller pages, if disabled elmah pages will not work -->
    <add key="elmah.mvc.disableHandleErrorFilter" value="true" /> <!-- This uses the default filter for elmah, set to disabled to use our own -->
    <add key="elmah.mvc.requiresAuthentication" value="false" /> <!-- Manages authentication for elmah pages -->
    <add key="elmah.mvc.allowedRoles" value="*" /> <!-- Manages authentication for elmah pages -->
    <add key="elmah.mvc.route" value="errortracking" /> <!-- Base route for elmah pages -->
    

    这里重要的是“elmah.mvc.disableHandleErrorFilter”,如果这是 false,它将使用 elmah.mvc 中的处理程序,该处理程序将通过使用默认的 HandleErrorHandler 来实际处理异常,该处理程序将忽略您的 customError 设置

    此设置允许您在类和视图中设置自己的 ErrorHandler 标记,同时仍通过 ElmahMVCErrorFilter 记录这些错误,通过 elmah 模块将 customError 配置添加到 web.config,甚至编写自己的错误处理程序。您唯一需要做的就是记住不要在我们编写的 elmah 过滤器之前添加任何实际处理错误的过滤器。我忘了提一下:elmah 中没有重复项。

    【讨论】:

      【解决方案8】:

      一个完全替代的解决方案是不使用 MVC HandleErrorAttribute,而是依靠 Elmah 设计的 ASP.Net 错误处理。

      您需要从 App_Start\FilterConfig(或 Global.asax)中删除默认的全局 HandleErrorAttribute,然后在您的 Web.config 中设置错误页面:

      <customErrors mode="RemoteOnly" defaultRedirect="~/error/" />
      

      注意,这可以是一个 MVC 路由 URL,所以上面会在发生错误时重定向到 ErrorController.Index 操作。

      【讨论】:

      • 这是迄今为止最简单的解决方案,默认重定向可以是 MVC 动作 :)
      • 这将重定向其他类型的请求,如 JSON 等——不好。
      猜你喜欢
      • 1970-01-01
      • 2011-04-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多