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

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

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

如何修改或如何修改属性,ELMAH可以知道有错误并logging下来。

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

您可以HandleErrorAttribute并覆盖其OnException成员(不需要复制),以便使用ELMAHloggingexception,并且只有在基础实现处理它时。 您需要的最less量的代码如下所示:

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

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

 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的错误信号 ,其中包括完全configuration的pipe道,如日志logging,邮件发送,过滤和你有什么。 否则,它会尝试查看是否应该过滤错误。 如果不是,则只logging错误。 此实现不处理邮件通知。 如果可以发送exception信号,则会发送邮件,如果configuration为这样的话。

如果多个HandleErrorAttribute实例有效,则可能还需要注意,重复日志logging不会发生,但是上面的两个示例应该会启动。

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

 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改进的解决scheme,以及处理MVC路由中的elmah接口的控制器(不再需要使用axd)
这个解决scheme(以及所有的问题)的问题是,这个或那个elmaherror handling程序实际上是处理错误,忽略你可能想要设置为一个customError标记,或通过ErrorHandler或你自己的error handling程序
最好的解决scheme恕我直言,是创build一个filter,将在所有其他filter的末尾行事,并logging已经处理的事件。 elmah模块应该注意logging应用程序未处理的其他错误。 这也将允许您使用健康监视器和可以添加到asp.net的所有其他模块来查看错误事件

我用elmah.mvc里面的ErrorHandler写了这个reflection器

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

现在,在你的过滤configuration你想要做这样的事情:

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

请注意,我在那里留下了一条评论,提醒人们如果他们想要添加一个全局filter来处理这个exception,它应该在最后一个filter之前进行,否则就会遇到ElmahMVCErrorFilter会忽略未处理的exception的情况,它没有被处理,它应该被Elmah模块所占用,但是接下来的filter将exception标记为已处理,并且模块将忽略该exception,从而导致不会将其变为elmah的exception。

现在,确保您的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”,如果这是错误的,它将使用elmah.mvc中的处理程序,通过使用默认的HandleErrorHandler来处理exception,这将忽略您的customError设置

这个设置允许你在类和视图中设置你自己的ErrorHandler标签,同时仍然通过ElmahMVCErrorFilterlogging这些错误,通过elmah模块添加一个customErrorconfiguration到你的web.config,甚至编写你自己的error handling程序。 你唯一需要做的就是记住不要在我们写的elmahfilter之前添加任何实际上处理错误的filter。 我忘了提及:在elmah没有重复。

你可以通过引入一个自定义的控制器工厂,将HandleErrorWithElmah属性注入到每个控制器中,从而获得上述代码。

欲了解更多信息,请查看我的博客系列MVClogin。 第一篇文章涵盖了让Elmah成立并运行MVC。

在文章结尾处有一个可下载代码的链接。 希望有所帮助。

http://dotnetdarren.wordpress.com/

我是新的ASP.NET MVC。 我面临同样的问题,下面是我的Erorr.vbhtml(它工作,如果你只需要logging错误使用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> 

简单!

一个完全替代的解决scheme是不使用MVC HandleErrorAttribute ,而是依赖于Elmahdevise用于处理的ASP.Neterror handling。

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

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

请注意,这可以是一个MVC路由URL,所以当发生错误时,上述操作将redirect到ErrorController.Index操作。

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

我希望这会帮助别人:)

这正是我需要我的MVC网站configuration!

我按照Atif Aziz的build议,对OnException方法添加了一些修改来处理多个HandleErrorAttribute实例:

请记住,如果多个HandleErrorAttribute实例有效,则可能必须小心,那么重复logging不会发生。

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

希望它是有用的:

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