Custom ExceptionHandling Logging Provider

Topics: Building and extending application blocks, Exception Handling Application Block, Logging Application Block
Dec 22, 2011 at 5:54 PM

Hello,

I am having trouble with what I thought would be an easy to implement idea. Here is from an MSDN post on the same topic.

I am having difficulty determining (debugging) why my custom assembly runs fine when called directly but not when put it in the web config for a wcf service. What I have done is take the Enterprise Library ExceptionHandling Logging source code and modified it (created new project /assembly) to log to the AppFabric.
For example when I reference the compiled assembly, or the project then instantiate the object it executes smoothly.
 
Type X = typeof(Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter);
LogWriter lwr = Microsoft.Practices.EnterpriseLibrary.Common.Configuration.EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();
AppFabricEventProvider affevt = new AppFabricEventProvider();

AppFabricLoggingExceptionHandler asdf = new AppFabricLoggingExceptionHandler("CTL_Whatever", 777, System.Diagnostics.TraceEventType.Error, "Me Title", 007, X, lwr, 7007007, ref affevt, "My Event", "Var1:{0}, Var2:{1}", "T", "15");
asdf.HandleException(new System.InvalidOperationException("Blah"), Guid.NewGuid());

 
However when I try to incorporate into the ExceptionShielding policy it acts as though it does not instantiate. What I am at a loss for is how can I trace through the debugger to see where it is failing as no errors appear to be thrown???
 
[ExceptionShielding("Nada")]
public class Service1 : IService1
{
     public string GetData(int value)
      {
          throw new System.InvalidOperationException("FINALLY");
          return string.Format("You entered: {0}", value);
      }
}

Web Config:
....
<exceptionHandlers>
     <add name="AppFabric Logging Exception Handler" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.AppFabricLogging.AppFabricLoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.AppFabricLogging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=50abcc6da8f29fee" logCategory="InvalidOperations" eventId="1001001" severity="Error" title="Enterprise Library AppFabric Exception Handling" formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling" priority="0" />

</exceptionHandlers>
....

Other exception handlers work fine so it is something with my custom object I just can't figure out what???
TIA
JB
Dec 23, 2011 at 2:44 AM

Are you saying that other exception handlers work fine when applied to an exception shielding policy?  Does your shielding policy work when configured in web.config but not invoked using Exception Shielding?  E.g. Does this work when the policy  "Policy" contains your app fabric log handler?

            catch (Exception ex)
            {
                bool retry = ExceptionPolicy.HandleException(ex, "Policy");
                if (retry)
                {
                    throw ex;
                }
            }

Your exception handling configuration should look something like:

  <exceptionHandling>
    <exceptionPolicies>
      <add name="Policy">
        <exceptionTypes>
          <add name="All Exceptions" type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="ThrowNewException">
            <exceptionHandlers>
              <add name="Logging Exception Handler" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                logCategory="General" eventId="100" severity="Error" title="Enterprise Library Exception Handling"
                formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"
                priority="0" />
              <add type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.FaultContractExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                faultContractType="WcfEhab_NoAttribute.MyFaultContract, WcfEhab_NoAttribute"
                name="Fault Contract Exception Handler">
                <mappings>
                  <add source="Message" name="MyMessage" />
                </mappings>
              </add>
            </exceptionHandlers>
          </add>
        </exceptionTypes>
      </add>
    </exceptionPolicies>
  </exceptionHandling>

Except instead of Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler it would have your Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.AppFabricLogging.AppFabricLoggingExceptionHandler.

 If the above doesn't help then I would suggest posting your full configuration.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com

Dec 27, 2011 at 2:19 PM

Well I feel pretty dumb. I should have thought to call the Policy directly but I didn't. :(  Anyway when I invoke the policy it now throws an error and the error makes sense only I really would have expected the error to be thrown when invoked by IIS from the web config. Anyway the following:

try
{
     throw new System.InvalidOperationException("FINALLY");
}
catch(InvalidOperationException ex)
{
     bool retry = ExceptionPolicy.HandleException(ex, "Nada");
}

Produces:

The type Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.AppFabricLogging.AppFabricLoggingExceptionHandler does not have a constructor that takes the parameters (String, Int32, TraceEventType, String, Int32, Type, LogWriter, Int32, AppFabricEventProvider, String, String, Object[]).

This makes sense as my custom version has a constructor that expects more objects than the default one. So do I revert the constructor back to the default and then in the implementation of the constructor call a private method that somehow gets a handle to the needed objects for AppFabric logging? Is there a way to provide a reference to those objects via the web config? I guess my question is what is the best approach to getting the handle / values of these additional objects to the HandleException method?

Constructor:

public AppFabricLoggingExceptionHandler(
            string logCategory,
            int eventId,
            TraceEventType severity,
            string title,
            int priority,
            Type formatterType,
            LogWriter writer,
            int E2EActivityID, //JB
            ref AppFabric.AppFabricEventProvider AppFabricEventProvider, //JB
            string AppFabricEvtName, //JB
            string AppFabricEvtMsgFormat, //JB
            params object[] AppFabricEvtMsgArgs //JB

            )
        {
            this.logCategory = logCategory;
            this.eventId = eventId;
            this.severity = severity;
            this.defaultTitle = title;
            this.minimumPriority = priority;
            this.formatterType = formatterType;
            this.logWriter = writer;

            this.E2EActivityID = E2EActivityID; //JB might not need this as it is handled at the trace level.
            this.AppFabricEvtProvider = AppFabricEventProvider; //JB
            this.AppFabricEvtName = AppFabricEvtName; //JB
            this.AppFabricEvtMsgFormat = AppFabricEvtMsgFormat; //JB
            this.AppFabricEvtMsgArgs = AppFabricEvtMsgArgs; //JB

        }

Of course the next question is can I dump the "event logging" fields as I don't use them. My exception handling policy handles the logging to the event log with a seperate handler.

 

Dec 28, 2011 at 2:35 AM

What are you using for a configuration data class?  Do you have a class such as AppFabricLoggingExceptionHandlerData?

In that class you would have a GetRegistrations method that "news up" the handler instance.  In that method you can invoke any constructor you want.  E.g.:

   public override IEnumerable<TypeRegistration> GetRegistrations(string namePrefix)
   {
        yield return new TypeRegistration<IExceptionHandler>( () => 
            new AppFabricLoggingExceptionHandler(E2EActivityID, AppFabricEventProvider, 
AppFabricEvtName, AppFabricEvtMsgFormat, AppFabricEvtMsgArgs)) { Name = BuildName(namePrefix), Lifetime = TypeRegistrationLifetime.Transient }; }

If you are still having an issue it would help to post the code and configuration.

I would also recommend looking at the Extensibility Hands-on Labs for Microsoft Enterprise Library 5.0 which has an example of a custom handler (if you haven't done that already).

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Dec 29, 2011 at 11:09 AM

Well, I didn't rename the class I just left it at the default name LoggingExceptionHandlerData. I do have the GetRegistrations as:

/// <summary>
        /// TODOC : review
        /// </summary>
        /// <param name="namePrefix"></param>
        /// <returns></returns>
        public override IEnumerable<TypeRegistration> GetRegistrations(string namePrefix)
        {
            //TODO: Just compile baby
            AppFabric.AppFabricEventProvider QQQ = new AppFabric.AppFabricEventProvider();

            yield return new TypeRegistration<IExceptionHandler>(
                () =>
                new AppFabricLoggingExceptionHandler(LogCategory, EventId, Severity, Title, Priority, FormatterType,
                                            Common.Configuration.ContainerModel.Container.Resolved<LogWriter>(), 777, ref QQQ, "DonaldDuck", "{0}", "T", "15"))
                       {
                           Name = BuildName(namePrefix),
                           Lifetime = TypeRegistrationLifetime.Transient
                       };
        }

I'll post the code and review the Hands on Lab again for something I missed first time through.

Thank You

JB

Dec 29, 2011 at 11:36 AM
Edited Dec 29, 2011 at 2:25 PM

Well, I can't find where to post source code so I put it here (www.gpgvm.org/wcfservice2.zip). Feel free to lambast me on everything I am doing wrong. It will help me become a better developer.

So in the code you'll see that the LoggingExceptionHandlerData (file named AppFabricExceptionHandlerData) class has three constructors and I believe this to be part of my confusion. All three constructors do NOT have a signature that jive with my AppFabricLoggingExceptionHandler....but why should they???

Dec 30, 2011 at 2:23 AM

Thanks for the code.  

The main problem with locating the constructor was the ref modifier.  Remove ref from the constructor everything seems to work OK.  That shouldn't be an issue since ref doesn't appear to be required here.

Here are some things for you to do:

  • Rename the HandlerData class to be AppFabricLoggingExceptionHandlerData
  • Create a constructor in AppFabricLoggingExceptionHandlerData class that accepts the information required for your handler (right now you are taking the logging data and not your app fabric data and hard coding the app fabric data)
  • Create properties that you require for configuring your handler and set those values in the constructor
  • Modify GetRegistrations to create and return an instance of your AppFabricLoggingExceptionHandler populated with the properties set in the constructor

Also note that the values set in GetRegistration are what gets passed to the actual instance of your handler.  This is what sets the configuration file values into your handler.  So you need to replace 777, DonaldDuck, etc. with properties such as E2eActivityId and AppFabricEventId that are set in the configuration file, passed into the constructor and then assigned to your properties..

public override IEnumerable<TypeRegistration> GetRegistrations(string namePrefix)
{
    //TODO: Just compile baby
    AppFabric.AppFabricEventProvider QQQ = new AppFabric.AppFabricEventProvider();

    yield return new TypeRegistration<IExceptionHandler>(
        () =>
        new AppFabricLoggingExceptionHandler(777, QQQ, "DonaldDuck", "String{0}{1}", "T", "15"))
                {
                    Name = BuildName(namePrefix),
                    Lifetime = TypeRegistrationLifetime.Transient
                };
}

You also might want to remove unnecessary code from when you copied the LoggingExceptionHandlerData.cs class such as:

    [AddSateliteProviderCommand(LoggingSettings.SectionName, typeof(LoggingSettings), "DefaultCategory", "LogCategory")]

Also, I'm confused by the ExceptionShielding usage.  The service is shielded with the attribute    [ExceptionShielding("Nada")].  But the service code looks like this:

            try
            {
                throw new System.InvalidOperationException("FINALLY");
            }
            catch(InvalidOperationException ex)
            {
                bool retry = ExceptionPolicy.HandleException(ex, "Nada");
            }

So, they both use the same policy which can be OK.  But in the configuration InvalidOperationException gets replaced with HttpException which is then rethrown.  Then the ExceptionShielding will be invoked for the same policy but there are no handlers configured for HttpException or any other base classes such as System.Exception so the Exception Shielding isn't actually doing anything.  Are you planning on using the fault contract exception handler?  This is usually what is applied at the service boundary to map exceptions to fault contracts. 

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Jan 4, 2012 at 6:55 PM

Quote snip:

Also, I'm confused by the ExceptionShielding usage. The service is shielded with the attribute [ExceptionShielding("Nada")]. But the service code looks like this:

            try
            {
                throw new System.InvalidOperationException("FINALLY");
            }
            catch(InvalidOperationException ex)
            {
                bool retry = ExceptionPolicy.HandleException(ex, "Nada");
            }

So, they both use the same policy which can be OK....

end snip.

Not done that way by design. I did that to force my policy to be used and see where it was having trouble. The shielding will be used and the catch block removed.

Jan 4, 2012 at 7:03 PM

OK -- fair enough.   :)

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com

Jan 4, 2012 at 8:15 PM

You don't have to respond to my running commentary. :)

This sentance explains some of my difficulty.....which is what you were alluding to earlier in the thread.

This lambda expression is

not executed; instead, the underlying system picks it apart and and turns it into the appropriate DI container registrations to do the same thing.