首页 文章

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

提问于
浏览
557

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

正如我猜测它,因为ELMAH只记录未处理的错误,[HandleError]属性正在处理错误,因此无需记录它 .

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

Edit: 让我确保每个人都明白,我知道我可以修改那个不是问题的属性我认为有错误,因为它已经被属性处理了......我要问的是有没有办法让ELMAH看到错误并记录它,即使属性处理它...我搜索周围,没有看到任何方法来调用强制它来记录错误....

8 回答

  • 6

    我是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.
    @item.ActionName
    @item.ControllerName
    @item.Exception.Message </h2>

    简直就是这样!

  • 2

    您可以继承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 实例生效,则不会发生重复日志记录,但上述两个示例应该启动 .

  • 7

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

    对不起,但我认为接受的答案是矫枉过正 . 你需要做的就是:

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

    现在在NuGet中有一个ELMAH.MVC包,其中包括Atif的改进解决方案以及处理MVC路由中的elmah接口的控制器(不再需要使用该axd)
    该解决方案(以及所有这些解决方案)的问题在于elmah错误处理程序实际上处理错误,忽略了您可能想要设置为customError标记或通过ErrorHandler或您自己的错误处理程序的问题 .
    最好的解决方案是IMHO创建一个过滤器,它将在所有其他过滤器的末尾起作用,并记录已经处理过的事件 . 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”,如果这是假的,它将使用elmah.mvc中的处理程序,该处理程序将使用默认的HandleErrorHandler实际处理异常,该默认HandleErrorHandler将忽略您的customError设置

    此设置允许您设置自己的设置类和视图中的ErrorHandler标记,同时仍然通过ElmahMVCErrorFilter记录这些错误,通过elmah模块向web.config添加customError配置,甚至编写自己的错误处理程序 . 您唯一需要做的就是记住在我们编写的elmah过滤器之前不添加任何实际处理错误的过滤器 . 我忘了提到:elmah没有重复 .

  • 497

    一个完全替代的解决方案是不使用MVC HandleErrorAttribute ,而是依赖于Elmah旨在使用的ASP.Net错误处理 .

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

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

    请注意,这可以是MVC路由URL,因此上述操作会在发生错误时重定向到 ErrorController.Index 操作 .

  • 6

    您可以采用上面的代码,并通过引入一个自定义控制器工厂更进一步,该工厂将HandleErrorWithElmah属性注入每个控制器 .

    有关更多信息,请查看我关于登录MVC的博客系列 . 第一篇文章介绍了如何让Elmah为MVC设置和运行 .

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

    http://dotnetdarren.wordpress.com/

  • 14

    对我来说,让电子邮件日志记录工作非常重要 . 经过一段时间后,我发现在Atif示例中只需要2行代码 .

    public class HandleErrorWithElmahAttribute : HandleErrorAttribute
    {
        static ElmahMVCMailModule error_mail_log = new ElmahMVCMailModule();
    
        public override void OnException(ExceptionContext context)
        {
            error_mail_log.Init(HttpContext.Current.ApplicationInstance);
            [...]
        }
        [...]
    }
    

    我希望这会帮助别人:)

相关问题