如何让ELMAH使用ASP.NET MVC [HandleError]属性?

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

正如我猜测它,因为ELMAH只记录未处理的错误和[HandleError]属性正在处理错误,因此不需要记录它。

如何修改或如何修改属性,ELMAH可以知道出现错误并记录下来。

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


您可以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实例有效,则可能还需要注意,重复日志记录不会发生,但上面的两个示例应该可以启动。


对不起,但我认为接受的答案是矫枉过正。 所有你需要做的是这样的:

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

现在,NuGet中有一个ELMAH.MVC包,它包含Atif改进的解决方案,以及处理MVC路由中的elmah接口的控制器(不再需要使用axd)
该解决方案(以及所有这些解决方案)的问题在于,这种或那种方式的elmah错误处理程序实际上是在处理错误,忽略了您可能想要设置为customError标记的内容,或者通过ErrorHandler或您自己的错误处理程序
IMHO的最佳解决方案是创建一个过滤器,它将在所有其他过滤器的末尾执行操作并记录已经处理的事件。 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的appsetting看起来像这样:

<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来处理异常,该HandleErrorHandler将忽略customError设置

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

链接地址: http://www.djcxy.com/p/20379.html

上一篇: How to get ELMAH to work with ASP.NET MVC [HandleError] attribute?

下一篇: Web API in MVC solution in separate project