Search code examples
asp.netasp.net-mvcasp.net-mvc-3health-monitoring

Health Monitoring is not logging errors when CustomError is set to "On" or "RemoteOnly"


I am using Health Monitoring for catching all errors and sending them to email. While it works in the development environment it did not when I deploy it in Prod. The only difference being the "customerrors" set to "on/off". So, I verified it again and it seems it will not log when the custom errors is set to "On/RemoteOnly". Below is part of my configuration in question.

Is there a workaround to this issue? Thanks.

<healthMonitoring enabled="true">
  <eventMappings>
    <clear />
    <add name="All Errors" type="System.Web.Management.WebBaseErrorEvent"
     startEventCode="0" endEventCode="2147483647" />
  </eventMappings>

  <providers>
    <clear />

    <add
     name="SimpleMailWebEventProvider"
     type="System.Web.Management.SimpleMailWebEventProvider"
     to="dev@net"
     from="de@net"
     buffer="false"
    />
  </providers>

  <rules>
    <clear />

    <add name="All Errors Default" eventName="All Errors" provider="SimpleMailWebEventProvider"
profile="Default" minInstances="1" maxLimit="Infinite" minInterval="00:00:00" />
  </rules>
</healthMonitoring>

--update This is MVC3 project


Solution

  • In an ASP.NET MVC 3 project you will have a global HandleError action filter registered by default in global.asax.cs:

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
    }
    

    This attribute gets applied to every controller action and if customErrors are set to On only the custom error page is displayed and the exception that occured in a controller action is marked as handled. ASP.NET Health Monitoring doesn't see this exception anymore and can't log it.

    An approach to use Health Monitoring together with the HandleError attribute and a custom error page is described here and here and here:

    You create a custom error attribute derived from HandleError:

    public class HandleErrorHealthMonitoringAttribute : HandleErrorAttribute
    {
        public override void OnException(ExceptionContext filterContext)
        {
            // Do the default, i.e. show custom error page if custom errors are on
            base.OnException(filterContext);
    
            // Suppress raising the health monitoring event below if custom errors
            // are off. In that case health monitoring will receive the exception
            // anyway and raise the event
            if (!filterContext.HttpContext.IsCustomErrorEnabled)
                return;
    
            // Raise health monitoring event
            var errorEvent = new GenericWebRequestErrorEvent(
                "Unhandled exception occurred.", this,
                WebEventCodes.WebExtendedBase + 1, filterContext.Exception);
    
            errorEvent.Raise();
        }
    }
    

    And then register this attribute instead of default HandleError:

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorHealthMonitoringAttribute());
    }
    

    The GenericWebRequestErrorEvent is a custom error event derived from the base WebRequestErrorEvent. It doesn't do anything custom and only exists because WebRequestErrorEvent doesn't have any public constructors, so we can't use var errorEvent = new WebRequestErrorEvent(...):

    public class GenericWebRequestErrorEvent : WebRequestErrorEvent
    {
        public GenericWebRequestErrorEvent(string message, object eventSource,
            int eventCode, Exception exception) :
            base(message, eventSource, eventCode, exception)
        {
        }
    
        public GenericWebRequestErrorEvent(string message, object eventSource,
            int eventCode, int eventDetailCode, Exception exception) :
            base(message, eventSource, eventCode, eventDetailCode, exception)
        {
        }
    }
    

    Note, that you will receive an email titled with MyNamespace.GenericWebRequestErrorEvent and not with System.Web.Management.WebRequestErrorEvent and the event code will always be 100001 (= WebEventCodes.WebExtendedBase + 1).