我正在尝试使用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实例生效,则不会发生重复的日志记录,但上面的两个示例应该可以帮助您开始。
这正是我的MVC站点配置所需要的!
我添加了一个小修改OnException方法来处理多个HandleErrorAttribute实例,由Atif Aziz建议:
请记住,如果多个HandleErrorAttribute实例生效,则不会发生重复的日志记录。
我只是检查上下文。在调用基类之前调用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);
}
抱歉,但我认为公认的答案是一个过度。你所需要做的就是:
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());
}