如何让ELMAH与ASP一起工作。NET MVC [HandleError]属性?

我正在尝试使用ELMAH来记录我的ASP中的错误。NET MVC应用程序,然而,当我使用[HandleError]属性在我的控制器ELMAH不会记录任何错误时,他们发生。

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

我如何修改或如何修改属性,以便ELMAH可以知道有一个错误并记录它。

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

112584 次浏览

您可以子类化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实例生效,则不会出现重复的日志记录,但上面的两个示例应该可以帮助您开始。

您可以使用上面的代码,进一步引入一个自定义控制器工厂,该工厂将HandleErrorWithElmah属性注入到每个控制器中。

要了解更多信息,请查看我关于登录MVC的博客系列。第一篇文章介绍了如何设置Elmah并运行MVC。

在本文末尾有一个可下载代码的链接。希望这能有所帮助。

http://dotnetdarren.wordpress.com/

这正是我的MVC站点配置所需要的!

我对OnException方法做了一点修改,以处理多个HandleErrorAttribute实例,正如Atif Aziz建议的那样:

请记住,如果有多个HandleErrorAttribute实例生效,则不会发生重复日志记录。

我只是在调用基类之前检查context.ExceptionHandled,只是想知道是否有人在当前处理程序之前处理了异常 它适用于我,我发布了代码,以防其他人需要它,并询问是否有人知道我是否忽略了什么。< / p >

希望对大家有用:

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

对我来说,让电子邮件日志工作是非常重要的。一段时间后,我发现这只需要2行代码在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);
[...]
}
[...]
}

我希望这能帮助到一些人:)

我是ASP的新手。净MVC。我也遇到过同样的问题,以下是我在工作中犯的错误。vbhtml(如果你只需要使用Elmah log记录错误,它可以工作)

@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>

很简单!

抱歉,但我认为公认的答案是一个过度。你所需要做的就是:

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

现在有一个ELMAH。NuGet中的MVC包,包括Atif改进的解决方案,还有一个控制器,在MVC路由中处理elmah接口(不再需要使用那个axd)
该解决方案(以及这里的所有解决方案)的问题是,elmah错误处理程序以某种方式实际处理错误,忽略了您可能想要设置为customError标记或通过ErrorHandler或您自己的错误处理程序
依我看,最好的解决方案是创建一个过滤器,它将在所有其他过滤器的末尾起作用,并记录已经处理的事件。elmah模块应该负责记录应用程序未处理的其他错误。这还将允许您使用运行状况监控器和可以添加到asp.net的所有其他模块来查看错误事件

我在elmah.mvc中的ErrorHandler中使用reflector写了这个

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标记,同时仍然通过elmahvcerrorfilter记录这些错误,并将customError配置添加到您的web。通过elmah模块配置,甚至编写自己的错误处理程序。你需要做的唯一一件事就是记住不要在我们编写的elmah过滤器之前添加任何实际处理错误的过滤器。我忘了说,埃尔玛体内没有复制体。

一个完全替代的解决方案是不使用MVC HandleErrorAttribute,而是依赖于ASP。Net错误处理,Elmah的设计工作。

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

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

注意,这可以是一个MVC路由URL,因此当发生错误时,上面的URL将重定向到ErrorController.Index操作。