How to get ELMAH to work with ASP.NET MVC [HandleError] attribute?
我试图使用elmah在我的ASP.NET MVC应用程序中记录错误,但是当我在控制器上使用[handleError]属性时,elmah不会在发生错误时记录任何错误。
因为Elmah只记录未处理的错误,而[handleError]属性正在处理错误,所以不需要记录。
如何修改或如何修改属性,以便Elmah知道发生了错误并记录下来。
编辑:让我确保每个人都理解,我知道我可以修改属性,这不是我要问的问题…Elmah在使用handleError属性时被绕过,这意味着它不会看到错误,因为它已经由属性处理了…我要问的是,有一种方法可以让elmah看到错误并记录它,即使属性处理了它……我四处搜索,没有任何方法可以调用来强制它记录错误……
您可以子类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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); } } |
首先调用基本实现,使其有机会将异常标记为正在处理。只有这样,才会发出异常信号。上述代码很简单,如果在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | 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的错误信号,这涉及到完全配置的管道,如日志记录、邮件、过滤和您拥有的东西。如果失败,它将尝试查看是否应该过滤错误。如果没有,则只记录错误。此实现不处理邮件通知。如果可以发出异常信号,那么如果配置为发送邮件,则会发送邮件。
您可能还需要注意,如果多个
对不起,但我认为被接受的答案太过分了。你需要做的就是:
1 2 3 4 5 6 7 8 9 | 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中注册它(顺序很重要):
1 2 3 4 5 | public static void RegisterGlobalFilters (GlobalFilterCollection filters) { filters.Add(new ElmahHandledErrorLoggerFilter()); filters.Add(new HandleErrorAttribute()); } |
现在,Nuget中有一个elmah.mvc包,其中包括由atif改进的解决方案,以及在mvc路由中处理elmah接口的控制器(不再需要使用该axd)。这个解决方案(以及这里所有的解决方案)的问题在于,不管怎样,elmah错误处理程序实际上正在处理错误,忽略您可能希望设置为customError标记的内容,或者通过error handler或您自己的错误处理程序来设置的内容。IMHO的最佳解决方案是创建一个过滤器,该过滤器将在所有其他过滤器的末尾起作用,并记录已处理的事件。Elmah模块应负责将应用程序未处理的其他错误登录。这还允许您使用运行状况监视器和所有其他可以添加到ASP.NET的模块来查看错误事件。
我在elmah.mvc内部的错误处理程序中用reflector编写了这个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 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; } } |
现在,在您的过滤器配置中,您希望执行如下操作:
1 2 3 4 5 | 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()); } |
请注意,我在这里留下了一条注释,提醒人们,如果他们想添加一个实际处理异常的全局筛选器,它应该放在最后一个筛选器之前,否则您会遇到这样一种情况:未处理的异常将被elmahmvserrorfilter忽略,因为它没有被处理,并且应由elmah模块记录下来。但是,下一个过滤器将异常标记为已处理,模块将忽略它,导致异常永远不会进入Elmah。
现在,确保webconfig中elmah的应用程序设置如下所示:
1 2 3 4 5 | <!-- This handles elmah controller pages, if disabled elmah pages will not work --> <!-- This uses the default filter for elmah, set to disabled to use our own --> <!-- Manages authentication for elmah pages --> <!-- Manages authentication for elmah pages --> <!-- Base route for elmah pages --> |
这里重要的是"elmah.mvc.disablehandleerfrilter",如果这是错误的,它将使用elmah.mvc中的处理程序,该处理程序将使用默认的handleerfrandler处理异常,该处理程序将忽略您的客户错误设置。
此设置允许您在类和视图中设置自己的ErrorHandler标记,同时仍通过elmahmvserrorfilter登录这些错误,通过elmah模块向web.config添加customError配置,甚至编写自己的错误处理程序。您需要做的唯一一件事是,不要在我们编写的elmah过滤器之前添加任何实际处理错误的过滤器。我忘了提:埃尔玛没有复制品。
您可以使用上面的代码,通过引入一个自定义控制器工厂,将handleErrorWithelmah属性注入到每个控制器中,从而更进一步。
有关更多信息,请查看我的关于登录MVC的博客系列。第一篇文章介绍如何为MVC设置和运行Elmah。
文章结尾有一个可下载代码的链接。希望有帮助。
网址:http://dotnedarren.wordpress.com/
另一种完全不同的解决方案是不使用MVC
您需要从app ou startfilterconfig(或global.asax)中删除默认的global
1 | <customErrors mode="RemoteOnly" defaultRedirect="~/error/" /> |
注意,这可能是一个MVC路由的URL,因此当发生错误时,上面的内容将重定向到
我是ASP.NET MVC的新手。我也面临同样的问题,下面是我在erorr.vbhtml中的可操作性(如果只需要使用elmah日志记录错误,它就可以工作)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @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 Sorry, an error occurred while processing your request.<br /> @item.ActionName<br /> @item.ControllerName<br /> @item.Exception.Message |
很简单!
对我来说,让电子邮件记录工作非常重要。过了一段时间,我发现在atif示例中,这只需要两行代码。
1 2 3 4 5 6 7 8 9 10 11 | 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站点配置所需要的!
我对
bear in mind that you may have to take care that if multiple
HandleErrorAttribute instances are in effect then duplicate logging does not occur.
我只是在调用基类之前检查
希望它有用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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); } |