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

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

如何修改或如何去修改该属性,所以ELMAH可以知道有错误并记录下来。.

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

评论

哇,我希望Jeff或Jared会回答这个问题。他们使用ELMAH进行Stackoverflow;)

嗯,很奇怪-我们不使用HandleErrorAttribute-在我们的web.config的部分中设置了Elmah。使用HandleErrorAttribute有好处吗?

@Jarrod-很高兴看到您的ELMAH前叉有什么“习惯”。

@dswatik您还可以通过在web.config中将redirectMode设置为ResponseRewrite来防止重定向。见blog.turlov.com/2009/01/…

我一直在浏览网络文档,并发表有关[HandleError]属性和Elmah的帖子,但是在设置虚拟案例时,我没有看到解决的行为(例如Elmah未记录“处理的”错误)。这是因为从Elmah.MVC 2.0.x开始,不再需要此自定义HandleErrorAttribute。它包含在nuget软件包中。

#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,涉及完全配置的管道,例如日志记录,邮件,过滤以及您所拥有的。如果失败,它将尝试查看是否应过滤错误。如果不是,则仅记录错误。此实现不处理邮件通知。如果可以发出异常信号,则将发送邮件(如果已配置)。

您还必须注意,如果多个HandleErrorAttribute实例有效,则不会发生重复日志记录,但是以上两个示例应该可以让您入门。

评论


优秀的。我根本没有尝试实施Elmah。我只是试图以一种可以与MVC很好地结合使用的方式来报告自己使用多年的错误报告。您的代码给了我一个起点。 +1

–史蒂夫·沃瑟姆(Steve Wortham)
09年11月18日在3:29

您不需要子类HandleErrorAttribute。您可以简单地使用IExceptionFilter实现,并将其与HandleErrorAttribute一起注册。另外,我不明白为什么要在ErrorSignal.Raise(..)失败的情况下进行回退。如果管道配置不正确,则应将其修复。对于5线性IExceptionFilter检查点4,在这里-ivanz.com/2011/05/08/…

–伊凡·兹拉捷夫(Ivan Zlatev)
2011年5月9日12:22



请您在@IvanZlatev的适用性,缺点等方面对以下答案进行评论。人们评论说,它更容易/更简短/更简单,并且与您的答案相同,因此应标记为正确答案。对此有您的看法并通过这些答案获得一定的清晰度将是很好的。

–安德鲁(Andrew)
2012年6月22日在16:39

这是否仍然相关或ELMAH.MVC是否可以处理?

–罗米亚斯
2014年4月3日13:45

甚至我也想知道它在今天的版本中是否仍然有用

–重构
2015年6月15日10:16



#2 楼

抱歉,但我认为已被接受的答案过高。您需要做的就是:

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。这是公认的答案。

– CallMeLaNN
2011年6月10日7:35

@bigb我认为您必须将异常包装在您自己的异常类型中,才能将内容附加到异常消息中,等等(例如new UnhandledLoggedException(抛出异常),它会在返回消息之前将某些内容附加到Message中。

–伊凡·兹拉捷夫(Ivan Zlatev)
11年7月27日在22:24

阿提夫·阿齐兹(Atif Aziz)创建了ELMAH,我愿意回答他

– jamiebarrow
2011年8月5日,9:01

@jamiebarrow我没有意识到这一点,但是他的回答是2岁左右,可能已经简化了API,以一种更短距离,更独立的方式支持问题的用例。

–伊凡·兹拉捷夫(Ivan Zlatev)
2011年8月5日在20:38



@Ivan Zlatev真的无法开始工作ElmahHandledErrorLoggerFilter()elmah只是记录未处理的错误,但未处理。正如您提到的,我以正确的顺序注册了过滤器,有什么想法吗?

– kuncevic.dev
2011年9月14日下午6:21

#3 楼

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

我用反射器在elmah.mvc中的ErrorHandler上编写了此外观br />
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的应用设置如下所示:

<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中没有重复。

#4 楼

您可以采用上面的代码,并通过引入一个自定义控制器工厂来进一步前进,该工厂将HandleErrorWithElmah属性注入到每个控制器中。

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

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

http://dotnetdarren.wordpress.com/

评论


在我看来,将其放在基本控制器类上会容易得多!

–内森·泰勒(Nathan Taylor)
2010年7月28日在6:30

上面关于日志和异常处理的Darren系列非常值得一读!!!很彻底!

–瑞安·安德森(Ryan Anderson)
2010年8月19日下午5:58

#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> 


它是简单!

评论


到目前为止,这是最简单的解决方案。无需编写或注册自定义处理程序和内容。对我来说很好

– ThiagoAlves
11年8月27日在15:38

对于任何JSON /非HTML响应,都将被忽略。

– Craig Stuntz
2012年1月13日在22:33

这也是在视图中执行服务级别的功能。不属于这里。

– Trevor de Koekkoek
2012年12月30日下午2:31

#6 楼

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

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

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


注意,这可以是MVC路由的URL,因此上面发生错误时将重定向到ErrorController.Index操作。

评论


到目前为止,这是最简单的解决方案,默认重定向可以是MVC操作:)

–杰里米·库克(Jeremy Cook)
13年8月29日在21:44

它将重定向到其他类型的请求,例如JSON等-不好。

–zvolkov
14年2月14日在21:42

#7 楼

对我来说,使电子邮件记录正常工作非常重要。一段时间后,我发现在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);
        [...]
    }
    [...]
}


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

#8 楼

这正是我的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 || ...)相互抵消,并且始终为true。我想念什么吗?

– joelvh
2011-1-18在18:17



首先,我检查在当前事件之前调用的其他任何Handler是否管理了该异常,并将结果存储在变量中:exceptionHandlerdByPreviousHandler。然后,我给当前处理程序一个机会来管理异常本身:base.OnException(context)。

–ilmatte
2011年6月13日在8:31



首先,我检查在当前事件之前调用的其他任何Handler是否管理了该异常,并将结果存储在变量中:exceptionHandlerdByPreviousHandler。然后,我给当前处理程序一个机会来管理异常本身:base.OnException(context)。如果以前未管理过异常,则可以是:1-由当前处理程序管理,然后:exceptionHandledByPreviousHandler = false和!context.ExceptionHandled = false 2-不由当前处理程序管理:ExceptionHandledByPreviousHandler = false和!context。 ExceptionHandled为true。只有情况1会记录。

–ilmatte
2011年6月13日在8:39