我正在尝试使用 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 中的 error signaling,这涉及到完全配置的管道,例如日志记录、邮件发送、过滤以及您所拥有的。如果做不到这一点,它会尝试查看是否应该过滤错误。如果不是,则简单地记录错误。此实现不处理邮件通知。如果可以发出异常信号,则如果配置为发送邮件,则会发送邮件。
您可能还需要注意,如果多个 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());
}
HandleErrorAttribute
,无需在 BaseController
上覆盖 OnException
。这是假设接受的答案。
new UnhandledLoggedException(Exception thrown)
在返回之前将某些内容附加到 Message
。
ElmahHandledErrorLoggerFilter()
elmah 只是记录未处理的错误,但未处理。正如你提到的那样,我以正确的顺序注册了过滤器,有什么想法吗?
NuGet 中现在有一个 ELMAH.MVC 包,其中包括 Atif 改进的解决方案以及处理 MVC 路由中的 elmah 接口的控制器(不再需要使用该 axd)该解决方案的问题(以及此处的所有问题) ) 是 elmah 错误处理程序实际处理错误的一种或另一种方式,而忽略了您可能希望设置为 customError 标记或通过 ErrorHandler 或您自己的错误处理程序设置的内容 恕我直言,最好的解决方案是创建一个过滤器,该过滤器将在所有其他过滤器的结尾并记录已经处理的事件。 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 的 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”,如果这是 false,它将使用 elmah.mvc 中的处理程序,该处理程序将通过使用默认的 HandleErrorHandler 来实际处理异常,该处理程序将忽略您的 customError 设置
此设置允许您在类和视图中设置自己的 ErrorHandler 标签,同时仍通过 ElmahMVCErrorFilter 记录这些错误,通过 elmah 模块将 customError 配置添加到 web.config,甚至编写自己的错误处理程序。您唯一需要做的就是记住不要在我们编写的 elmah 过滤器之前添加任何实际处理错误的过滤器。我忘了提:elmah 中没有重复项。
您可以通过引入将 HandleErrorWithElmah 属性注入每个控制器的自定义控制器工厂来更进一步地执行上面的代码。
有关更多信息,请查看我关于登录 MVC 的博客系列。第一篇文章介绍了如何为 MVC 设置和运行 Elmah。
文末有可下载代码的链接。希望有帮助。
http://dotnetdarren.wordpress.com/
我是 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>
简直了!
一个完全替代的解决方案是不使用 MVC HandleErrorAttribute
,而是依靠 Elmah 设计的 ASP.Net 错误处理。
您需要从 App_Start\FilterConfig(或 Global.asax)中删除默认全局 HandleErrorAttribute
,然后在 Web.config 中设置错误页面:
<customErrors mode="RemoteOnly" defaultRedirect="~/error/" />
请注意,这可以是 MVC 路由 URL,因此当发生错误时,上述内容将重定向到 ErrorController.Index
操作。
对我来说,让电子邮件记录正常工作非常重要。一段时间后,我发现在 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 站点配置所需要的!
根据 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);
}
不定期副业成功案例分享