我正在尝试使用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实例生效,则不会发生重复的日志记录,但上面的两个示例应该可以帮助您开始。


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

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

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

http://dotnetdarren.wordpress.com/


这正是我的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);
}

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

There is now an ELMAH.MVC package in NuGet that includes an improved solution by Atif and also a controller that handles the elmah interface within MVC routing (no need to use that axd anymore) The problem with that solution (and with all the ones here) is that one way or another the elmah error handler is actually handling the error, ignoring what you might want to set up as a customError tag or through ErrorHandler or your own error handler The best solution IMHO is to create a filter that will act at the end of all the other filters and log the events that have been handled already. The elmah module should take care of loging the other errors that are unhandled by the application. This will also allow you to use the health monitor and all the other modules that can be added to asp.net to look at error events

我在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。发生错误时索引操作。