Deriving from LoggingExceptionHandler?

Topics: Exception Handling Application Block
May 24, 2010 at 11:59 AM

I want to change the way exceptions are logged, can it looks like I should be able to do everything I need by creating a type the derives from LoggingExceptionHandler. However, I can't seem to get the exception handler to call my new type.

The signature of the type is:

    [ConfigurationElementType(typeof(LoggingExceptionHandlerData))]
    public class myExceptionLoggingHandler : LoggingExceptionHandler
    {
    }

The configuration I have is:

  <exceptionHandling>
    <exceptionPolicies>
      <add name="LoggingPolicy">
        <exceptionTypes>
          <add name="BaseExceptions" type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="NotifyRethrow">
            <exceptionHandlers>
              <add name="eBackPack Logging Exception Handler" type="Common.Logging.myExceptionLoggingHandler, Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb3f7e851625ad3"
                logCategory="General" eventId="401" severity="Error" title="Aggregation Service"
                formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"
                priority="0" />
            </exceptionHandlers>
          </add>
        </exceptionTypes>
      </add>
    </exceptionPolicies>
  </exceptionHandling>

I instantiate the unity container with the following code:
            unityContainer = new UnityContainer();
            unityContainer.RegisterType<myExceptionLoggingHandler>();
            unityContainer.AddNewExtension<EnterpriseLibraryCoreExtension>();
            var section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
            section.Configure(unityContainer, ConfigurationConstants.UnityContainerName);

            unityContainer.AddNewExtension<Interception>();
            unityContainer.RegisterType<IAggregationLogic<Announcement>, AnnouncementManager>
                (
                    new Interceptor(new InterfaceInterceptor()),
                    new InterceptionBehavior<PolicyInjectionBehavior>()
                );
Exception management is controlled by the ExceptionCallHandler attribute on the class:
    [ExceptionCallHandler("LoggingPolicy")]
However, when I run the code and catch an exception, the logging is done by the EntLib LoggingExceptionHandler.
What I am I doing wrong, and how to I get it to use my handler?

 

May 25, 2010 at 6:49 AM

Hi calvinandhobbes,

It sounds to me that what you're trying to do is to create a CustomException Handler, right? Please correct me if I'm wrong.

You cannot explicitly derive from the LoggingExceptionHandler if you intend to create a new ExceptionHandler. If yes, then  what you need is to have a class that implements the IExceptionHandler interface.

For more details check this references http://msdn.microsoft.com/en-us/library/ff664570(v=PandP.50).aspx and http://blogs.microsoft.co.il/blogs/kolbis/archive/2007/08/07/Creating-a-custom-exception-handler.aspx.

Gino Terrado
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com 

May 25, 2010 at 8:27 PM

1.) It would appear that this blog entry is either dated or wrong.  I have seen this blog referenced in a few places.  It has a class that explicity inherits from LoggingEnceptionHandler http://customdatabasetracelistener.blogspot.com/2009/06/custom-dbtracelistener-w-custom.html  Is this blog wrong?

2.) How can we pass custom application data to the handler for logging in the database?

 

thanks -dave

ie..

public class MyLoggingExceptionHandler : LoggingExceptionHandler, IExceptionHandler{
....

}

May 26, 2010 at 5:29 AM

No, the blog's fine, actually you could do that if you only need to modify the WriteToLog method of the loggingexception handler.  Your initial post shows that you didn't inherit from IExceptionHandler. 

Anyway, are you indeed trying to achieve full integration with the config tool?  Because if yes, I suggest you use a different ConfigurationElementType.  Create your own class inheriting from ExceptionHandlerData and use the same properties as those with the LoggingExceptionHandlerData. 

In your config, it is still using entlib's logging exception handler because you used the LoggingExceptionHandlerData as the configurationtypeelement. The logic in the GetRegistrations method of that class creates typeregistrations for the LoggingExceptionHandler and not for your custom exception handler.  This is why I'm suggesting you specify a different configurationtypeelement.

On your second question, are those runtime values?  If yes, the only way I could think of passing those are adding them to the exception.Data collection property.

 

Sarah Urmeneta
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com 

May 26, 2010 at 12:22 PM
Edited May 26, 2010 at 12:25 PM

All I want to do is override the WriteToLog method, so I have made the class derive from LoggingExceptionHandler and implement IExceptionHandler interface.

I still can't get the type to load to actually handle the exception - LoggingExceptionHandler gets used every time.

I have registered the new type with the unity container as in the sample code above. I have tried also mapping the type to the IExceptionHandler interface, but still the type doesn't get loaded to handle an exception that is needs to be logged as part of the interception policy. I have tried to use a unity mapping of:

<register name="LoggingPolicy.BaseExceptions.eBackPack Logging Exception Handler" type="IExceptionHandler" mapTo="myExceptionLoggingHandler" />

(the aliases are mapped to the correct types)

but that results in a failure to instantiate the new type with the following error:

Resolving Common.Logging.myExceptionLoggingHandler,LoggingPolicy.BaseExceptions.eBackPack Logging Exception Handler (mapped from Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.IExceptionHandler, LoggingPolicy.BaseExceptions.eBackPack Logging Exception Handler)

Resolving parameter "logCategory" of constructor Common.Logging.myExceptionLoggingHandler(System.String logCategory, System.Int32 eventId, System.Diagnostics.TraceEventType severity, System.String title, System.Int32 priority, System.Type formatterType, Microsoft.Practices.EnterpriseLibrary.Logging.LogWriter writer) Resolving System.String,(none)

-----------------------------------------------

At the time of the exception, the container was: Resolving Services.Aggregation.Service.BusinessLogic.AnnouncementManager,(none) (mapped from Services.Aggregation.Service.BusinessLogic.IAggregationLogic`1[Services.Aggregation.ServiceContracts.Announcement], (none))

Resolving Microsoft.Practices.Unity.InterceptionExtension.PolicyInjectionBehavior,(none)

Calling constructor Microsoft.Practices.Unity.InterceptionExtension.PolicyInjectionBehavior(Microsoft.Practices.Unity.InterceptionExtension.CurrentInterceptionRequest interceptionRequest, Microsoft.Practices.Unity.InterceptionExtension.InjectionPolicy[] policies, Microsoft.Practices.Unity.IUnityContainer container)

---> Microsoft.Practices.Unity.ResolutionFailedException: Resolution of the dependency failed, type = "Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ExceptionPolicyImpl", name = "LoggingPolicy". Exception occurred while: while resolving.

Exception is: InvalidOperationException - The type String cannot be constructed. You must configure the container to supply this value.

So where to now?

May 26, 2010 at 4:16 PM

AvanadeSupport,

1.) Can you create a small sample based on this blog http://customdatabasetracelistener.blogspot.com/2009/06/custom-dbtracelistener-w-custom.html? You mentioned, that it is ok however when my handler inherits from LoggingExceptionHandler and implements IExceptionHandler I get odd can't create/build errors about missing constructor not taking 0 arguments.

2.) I am confused as to how i can best achieve logging custom exception data via exception handler, logging and trace listeners.  Can you explain how those components are working together.  Thx

 

May 27, 2010 at 4:31 AM

calvinandhobbles,

The exception you're getting is due to the fact that your custom exception handler has these parameters to its constructor which you did not configure in the container.  Thus, Unity is unable to create your custom handler.  You would need to configure them manually.  For me, I find this requires more effort than what I originally suggested, that is, to create a new class inheriting from ExceptionHandlerData and just copy what is in the LoggingExceptionHandlerData and use it as the ConfigurationElementType of your custom logging exception handler.

public class CustomLoggingHandlerData : ExceptionHandlerData
    {
         private static readonly AssemblyQualifiedTypeNameConverter typeConverter
            = new AssemblyQualifiedTypeNameConverter();

        private const string logCategory = "logCategory";
        private const string eventId = "eventId";
        private const string severity = "severity";
        private const string title = "title";
        private const string formatterType = "formatterType";
        private const string priority = "priority";
        private const string useDefaultLogger = "useDefaultLogger";

        public CustomLoggingHandlerData() : base(typeof(CustomLoggingExceptionHandler))
        {
        }

        public CustomLoggingHandlerData(string name,
                                           string logCategory,
                                           int eventId,
                                           TraceEventType severity,
                                           string title,
                                           Type formatterType,
                                           int priority)
            : this(name, logCategory, eventId, severity, title, typeConverter.ConvertToString(formatterType), priority)
        {
        }

        public CustomLoggingHandlerData(string name,
                                           string logCategory,
                                           int eventId,
                                           TraceEventType severity,
                                           string title,
                                           string formatterTypeName,
                                           int priority)
            : base(name, typeof(CustomLoggingExceptionHandler))
        {
            LogCategory = logCategory;
            EventId = eventId;
            Severity = severity;
            Title = title;
            FormatterTypeName = formatterTypeName;
            Priority = priority;
        }

        /// <summary>
        /// Gets or sets the default log category.
        /// </summary>
        [ConfigurationProperty(logCategory, IsRequired = true)]
        [Reference(typeof(NamedElementCollection<TraceSourceData>), typeof(TraceSourceData))]
        //[ResourceDescription(typeof(DesignResources), "LoggingExceptionHandlerDataLogCategoryDescription")]
        //[ResourceDisplayName(typeof(DesignResources), "LoggingExceptionHandlerDataLogCategoryDisplayName")]
        public string LogCategory
        {
            get { return (string)this[logCategory]; }
            set { this[logCategory] = value; }
        }

        /// <summary>
        /// Gets or sets the default event ID.
        /// </summary>
        [ConfigurationProperty(eventId, IsRequired = true, DefaultValue=100)]
        //[ResourceDescription(typeof(DesignResources), "LoggingExceptionHandlerDataEventIdDescription")]
        //[ResourceDisplayName(typeof(DesignResources), "LoggingExceptionHandlerDataEventIdDisplayName")]
        public int EventId
        {
            get { return (int)this[eventId]; }
            set { this[eventId] = value; }
        }

        /// <summary>
        /// Gets or sets the default severity.
        /// </summary>
        [ConfigurationProperty(severity, IsRequired = true, DefaultValue = TraceEventType.Error)]
        //[ResourceDescription(typeof(DesignResources), "LoggingExceptionHandlerDataSeverityDescription")]
        //[ResourceDisplayName(typeof(DesignResources), "LoggingExceptionHandlerDataSeverityDisplayName")]
        public TraceEventType Severity
        {
            get { return (TraceEventType)this[severity]; }
            set { this[severity] = value; }
        }

        /// <summary>
        ///  Gets or sets the default title.
        /// </summary>
        [ConfigurationProperty(title, IsRequired = true, DefaultValue="Enterprise Library Exception Handling")]
        //[ResourceDescription(typeof(DesignResources), "LoggingExceptionHandlerDataTitleDescription")]
        //[ResourceDisplayName(typeof(DesignResources), "LoggingExceptionHandlerDataTitleDisplayName")]
        public string Title
        {
            get { return (string)this[title]; }
            set { this[title] = value; }
        }

        /// <summary>
        /// Gets or sets the formatter type.
        /// </summary>
        public Type FormatterType
        {
            get { return (Type)typeConverter.ConvertFrom(FormatterTypeName); }
            set { FormatterTypeName = typeConverter.ConvertToString(value); }
        }

        /// <summary>
        /// Gets or sets the formatter fully qualified assembly type name.
        /// </summary>
        /// <value>
        /// The formatter fully qualified assembly type name
        /// </value>
        [ConfigurationProperty(formatterType, IsRequired = true)]
        //[ResourceDescription(typeof(DesignResources), "LoggingExceptionHandlerDataFormatterTypeNameDescription")]
        //[ResourceDisplayName(typeof(DesignResources), "LoggingExceptionHandlerDataFormatterTypeNameDisplayName")]
        [Editor(CommonDesignTime.EditorTypes.TypeSelector, CommonDesignTime.EditorTypes.UITypeEditor)]
        [BaseType(typeof(Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ExceptionFormatter))]
        [DesigntimeDefaultAttribute("Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling")]
        [Validation("Microsoft.Practices.EnterpriseLibrary.Configuration.Design.ViewModel.BlockSpecifics.Logging.LogFormatterValidator, Microsoft.Practices.EnterpriseLibrary.Configuration.DesignTime")]
        public string FormatterTypeName
        {
            get { return (string)this[formatterType]; }
            set { this[formatterType] = value; }
        }

        /// <summary>
        /// Gets or sets the minimum value for messages to be processed.  Messages with a priority
        /// below the minimum are dropped immediately on the client.
        /// </summary>
        //[ResourceDescription(typeof(DesignResources), "LoggingExceptionHandlerDataPriorityDescription")]
        //[ResourceDisplayName(typeof(DesignResources), "LoggingExceptionHandlerDataPriorityDisplayName")]
        [ConfigurationProperty(priority, IsRequired = true)]
        public int Priority
        {
            get { return (int)this[priority]; }
            set { this[priority] = value; }
        }

        /// <summary>
        /// Gets or sets the default logger to be used.
        /// </summary>
        [ConfigurationProperty(useDefaultLogger, IsRequired = false, DefaultValue = false)]
        [Obsolete("Behavior is limited to UseDefaultLogger = true")]
        [Browsable(false)]
        public bool UseDefaultLogger
        {
            get { return (bool)this[useDefaultLogger]; }
            set { this[useDefaultLogger] = value; }
        }

        public override IEnumerable<TypeRegistration> GetRegistrations(string namePrefix)
        {
            yield return new TypeRegistration<IExceptionHandler>(
                () =>
                new CustomLoggingExceptionHandler(LogCategory, EventId, Severity, Title, Priority, FormatterType,
                                         Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ContainerModel.Container.Resolved<LogWriter>()))
                       {
                           Name = BuildName(namePrefix),
                           Lifetime = TypeRegistrationLifetime.Transient
                       };
        }
    }

I just commented the ResourceDescription attributes as it refers to the DesignResource resource file from the ExceptionHandling project, you can simply create your own resource file if you want to make use of the attribute.

Sarah Urmeneta
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com  

May 27, 2010 at 4:47 AM

Thanks for the reply, but what I don't understand is why I can't just use the same ExceptionHandlerData that the default LoggingExceptionHandler uses? You will see that the class definition for myExceptionLoggingHandler is marked with the attribute:

[ConfigurationElementType(typeof(LoggingExceptionHandlerData))]

Surely that should be enough? The configuration data required is the same, so I didn't think I needed to create a new ExceptionHandlerData type.

Regards,

CalvinAndHobbes

May 27, 2010 at 4:55 AM

dyardy,

1. You should create a constructor for your custom class which takes the same parameters as the LoggingExceptionHandler does and then pass those parameters to the base constructor.

public MyLoggingExceptionHandler (string logCategory, int eventId,
            TraceEventType severity, string title, int priority,
            Type formatterType, LogWriter writer)
 : base(logCategory, eventId, severity, title, priority, formatterType, writer)
{
}

2. It depends, if you simply want to include extra information in your logs, the easiest way would be to include them in the exception.Data collection property and its contents map to the ExtendedProperties property of a LogEntry.  They are included in the log message using the  {dictionary({key} - {value}{newline})} token.  In the blog, it created a new class deriving from LogEntry and wants those properties to be logged in a separate column for a database table.  The general approach taken in the blog is appropriate if you want to log extra information in a database table. 

"Can you explain how those components are working together"

Basically, a logging exception handler would have a reference to LogWriter object which it will use to log the exception passed to it. It creates a LogEntry object out of the exception it is supposed to handle and pass it to the LogWriter object which in turn forwards it to the different trace listeners (determined by the exception handler's LogCategory) for processing.

 

Sarah Urmeneta
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com  

Jul 26, 2010 at 2:36 PM

Hi,

 

I want to log the exception details in FaultException<CustomErrorFault>(This is what is returned by my WCF services and I have no rights to change its implementation). I followed the above example. Is this the right way to implement.

1. I am getting build errors as 'Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler' does not contain a constructor that takes '0' arguments. Could you please let me know what is the problem.

2.If you notice exception is made available by having member veriable exception and populating it in HandleException. Is this correct?

3.Suggest if there is any better way to implement this. I haev also gone thru this article.. http://entlib.codeplex.com/Thread/View.aspx?ThreadId=61573 but also for formatter how would I make enble myerrorfault.

Thanks in advance..below is the code

   public class CustomLogEntry : LogEntry

   {

4. Have also gone through the

       public MyErrorFault MyErrFlt//this is the extra information i want to log

       { get; set; }

       public string MyErrFltDetails//this is the extra information i want to log

       { get; set; }

   }

 

 

   [ConfigurationElementType(typeof(CustomHandlerData))]

   [ConfigurationElementType(typeof(LoggingExceptionHandlerData))]

   public class MyExceptionHandler : LoggingExceptionHandler, IExceptionHandler

   {

       Exception ex;

 

       #region Constructors

 

       public MyExceptionHandler(NameValueCollection ignore)

       {

 

       }

 

       public MyExceptionHandler(string logCategory, int eventId,

           TraceEventType severity, string title, int priority,

           Type formatterType, LogWriter writer)

           : base(logCategory, eventId, severity, title, priority, formatterType, writer)

       {

       }

 

 

       //public MyExceptionHandler(System.String logCategory, System.Int32 eventId, System.Diagnostics.TraceEventType severity, System.String title, System.Int32 priority, System.Type formatterType, Microsoft.Practices.EnterpriseLibrary.Logging.LogWriter writer)

       //{

       //}

       #endregion

 

       #region LoggingExceptionHandler

       protected override void WriteToLog(string logMessage, IDictionary data)

       {

           CustomLogEntry log = new CustomLogEntry(); // I created a CustomLogEntry

 

           //log.MyErrFlt =

 

           log.Message = logMessage;

 

           log.Categories.Add("AuditFile");

 

           log.Severity = System.Diagnostics.TraceEventType.Error;

 

           foreach (DictionaryEntry dataEntry in ex.Data)

           {

               if (dataEntry.Key is string)

               {

 

                   log.ExtendedProperties.Add(dataEntry.Key as string,

 

                   dataEntry.Value);

               }

           }

 

           Logger.Write(log);

 

       }

       #endregion

 

 

       #region IExceptionHandler Members

       public Exception HandleException(Exception exception, Guid handlingInstanceId)

       {

           // Perform processing here. The exception you return will be

           // passed to the next exception handler in the chain.

           ex = exception;

           if (exception is FaultException<MyErrorFault>)

           {

               MyErrorFault fault = (exception as FaultException<MyErrorFault>).Detail;

               if (fault.Type != null)

               {

                   if (fault.Type.Contains("MyServiceException"))

                   {

                       MyServiceException objAepBusinessEx = new MyServiceException("MyServiceException Type", exception, fault);

                       return objAepBusinessEx;

                   }

               }

           }

           return exception;

       }

       #endregion

   }

Jul 27, 2010 at 7:27 AM

Hi sonali,

First thing, could you kindly elaborate more what are you trying to accomplish in your application. This will help us to have a clear understanding of what problem you are currently facing right now.

For item#1, I'm assuming what are you trying to do here is creating your own custom exception handler, right? If yes, then you wouldn't need to inherit from the LoggingExceptionHandler class, all you just need is to inherit from the IExceptionHandler interface. So your Custom Handler will look something like this. You can also refer to the documentation for more details http://msdn.microsoft.com/en-us/library/ff664570(v=PandP.50).aspx.

[ConfigurationElementType(typeof(CustomHandlerData))]    
    public class MyExceptionHandler : IExceptionHandler
    {
        #region Constructors
        public MyExceptionHandler(NameValueCollection ignore)
            : base()
        { }
        public MyExceptionHandler(string logCategory, int eventId,

            TraceEventType severity, string title, int priority,

            Type formatterType, LogWriter writer)

            : base()
        {}
        #endregion

        #region IExceptionHandler Members
        public Exception HandleException(Exception exception, Guid handlingInstanceId)
        {
            //Do whatever you would want here with the exception caught
            return exception;
        }
        #endregion

    }

For item#2, the HandleException method can be used on whatever you would want to do with the exception caught. Either log it, replace it or maybe display it depending on your needs. Take note that the exception returned from this method will be the exception that will be passed to the next exception handler in the chain if there is any.

For item#3, do you mean how you'll be able to create your own custom exception formatter? If yes, you can refer to the documentation for details http://msdn.microsoft.com/en-us/library/ff664587(v=PandP.50).aspx.

Gino Terrado
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com

 

 

 

Jul 27, 2010 at 2:27 PM

 

  1. I am setting interception ExceptionCallHandler:ICallHandler on one of the layers and have configured exception policy to catch the specific exceptions thrown by wcf services FaultExceptions<MyErrorFault> [WCF services are not configured to use wcf exception shielding with exception handling block]

2.  In my Invoke method of ExceptioncallHandler, I call IMethodReturn result = getNext()(input, getNext); which invokes method and when exception is thrown of above exception type [FaultException<MyErrorFault>, I have my own Handler class (to make it more generic) [ConfigurationElementType(typeof(CustomHandlerData))]

public class MyExceptionHandler : IExceptionHandler

 

       #region Constructors

       public MyExceptionHandler(NameValueCollection ignore)

       {

 

       }

       #endregion

 

       #region IExceptionHandler Members

       public Exception HandleException(Exception exception, Guid handlingInstanceId)

       {

           // Perform processing here. The exception you return will be

           // passed to the next exception handler in the chain.

 

           if (exception is FaultException<MyErrorFault> )

           {

             MyErrorFault fault = (exception as FaultException<MyErrorFault>).Detail;

             if (fault.Type != null)

             {

                 if (fault.Type.Contains("MyServiceException"))

                 {

                     MyServiceException objMyBusinessEx = new MyServiceException("MyServiceException Type", exception, fault);

 

                     return objMyBusinessEx;

                 }

             }

           }

 

 

           return exception;

       }

}

  1. In Policy web.config, I have configured to catch the faultexception<MyErrorFault> and specified handler as MyExceptionHandler.
  2. When I add log handler , using regular logging application block, it does not log the MyerrorFault actual message/ properties since it is not enterprise library thing. Logging handler overwrites and just displays the message as Creator of the fault did not specify the reason and looses the actual excpetion.
  3. I want to log MyErrorFault members. So I tried to follow the above article, deriving from LoggingExceptionHandler and also implementing IExceptionHandler and started getting erros/exception. Hope I am able to explain it well. What is the best way to handle this?

 

 

So far I have tried following approaches:

1. Adding a CustomLogFormatter

-             [ConfigurationElementType(typeof(CustomFormatterData))]

-             public class MyLogFormatter : LogFormatter //TextFormatter

-             {

-                 private readonly LogWriter logWriter;

-           

-                 /// <summary>

-                 /// Creates a <see cref="LogCallHandler"/> with default settings that writes

-                 /// to the default log writer.

-                 /// </summary>

-                 /// <remarks>See the <see cref="LogCallHandlerDefaults"/> class for the default values.</remarks>

-                 public MyLogFormatter(NameValueCollection ignored)

-                 {

-                 }

-           

-                 /// <summary>

-                 /// Creates a <see cref="LogCallHandler"/> with default settings that writes

-                 /// to the given <see cref="LogWriter"/>.

-                 /// </summary>

-                 /// <remarks>See the <see cref="LogCallHandlerDefaults"/> class for the default values.</remarks>

-                 /// <param name="logWriter"><see cref="LogWriter"/> to write logs to.</param>

-                 public MyLogFormatter(LogWriter logWriter)

-                 {

-                     EventId = MyLogFormatterDefaults.EventId;

-                     LogBeforeCall = MyLogFormatterDefaults.LogBeforeCall;

-                     LogAfterCall = MyLogFormatterDefaults.LogAfterCall;

-                     BeforeMessage = MyLogFormatterDefaults.BeforeMessage;

-                    AfterMessage = MyLogFormatterDefaults.AfterMessage;

-                     Categories = new List<string>();

-                     Severity = MyLogFormatterDefaults.Severity;

-                     Priority = MyLogFormatterDefaults.Priority;

-                     IncludeCallTime = MyLogFormatterDefaults.IncludeCallTime;

-                     IncludeParameters = MyLogFormatterDefaults.IncludeParameters;

-                     IncludeCallStack = MyLogFormatterDefaults.IncludeCallStack;

-                     this.logWriter = logWriter;

-                 }

-           

-                 /// <summary>

-                 /// Event ID to include in log entry

-                 /// </summary>

-                 /// <value>event id</value>

-                 public int EventId { get; set; }

-           

-                 /// <summary>

-                 /// Should there be a log entry before calling the target?

-                 /// </summary>

-                 /// <value>true = yes, false = no</value>

-                 public bool LogBeforeCall { get; set; }

-           

-                 /// <summary>

-                 /// Should there be a log entry after calling the target?

-                 /// </summary>

-                 /// <value>true = yes, false = no</value>

-                 public bool LogAfterCall { get; set; }

-           

-                 /// <summary>

-                 /// Message to include in a pre-call log entry.

-                 /// </summary>

-                 /// <value>The message</value>

-                 public string BeforeMessage { get; set; }

-           

-                 /// <summary>

-                 /// Message to include in a post-call log entry.

-                 /// </summary>

-                 /// <value>the message.</value>

-                 public string AfterMessage { get; set; }

-           

-                 /// <summary>

-                 /// Gets the collection of categories to place the log entries into.

-                 /// </summary>

-                 /// <remarks>The category strings can include replacement tokens. See

-                 /// the <see cref="CategoryFormatter"/> class for the list of tokens.</remarks>

-                 /// <value>The list of category strings.</value>

-                 public List<string> Categories { get; set; }

-           

-                 /// <summary>

-                 /// Should the log entry include the parameters to the call?

-                 /// </summary>

-                 /// <value>true = yes, false = no</value>

-                 public bool IncludeParameters { get; set; }

-           

-                 /// <summary>

-                 /// Should the log entry include the call stack?

-                 /// </summary>

-                 /// <remarks>Logging the call stack requires full trust code access security permissions.</remarks>

-                 /// <value>true = yes, false = no</value>

-                 public bool IncludeCallStack { get; set; }

-           

-                 /// <summary>

-                 /// Should the log entry include the time to execute the target?

-                 /// </summary>

-                 /// <value>true = yes, false = no</value>

-                 public bool IncludeCallTime { get; set; }

-           

-                 /// <summary>

-                 /// Priority for the log entry.

-                 /// </summary>

-                 /// <value>priority</value>

-                 public int Priority { get; set; }

-           

-                 /// <summary>

-                 /// Severity to log at.

-                 /// </summary>

-                 /// <value><see cref="TraceEventType"/> giving the severity.</value>

-                 public TraceEventType Severity { get; set; }

-           

-                 public override string Format(LogEntry logEntry)

-                 {

-                     MyMethodLogEntry MyLogEntry = new MyMethodLogEntry();

-                     MyLogEntry.Message = logEntry.Message;

-           

-                     var message = new StringBuilder();

-           

-                     if (!String.IsNullOrEmpty(logEntry.Message))

-                     {

-                        message.Append(message);

-                     }

-                     if (!String.IsNullOrEmpty(MyLogEntry.TypeName))

-                     {

-                         message.Append(";Type=");

-                         message.Append(MyLogEntry.TypeName);

-                     }

-                     if (!String.IsNullOrEmpty(MyLogEntry.MethodName))

-                     {

-                         message.Append(";Method=");

-                         message.Append(MyLogEntry.MethodName);

-                     }

-                     if (logEntry.ExtendedProperties.Count > 0)

-                     {

-                          message.Append(";Parameters={");

-                         foreach (var param in logEntry.ExtendedProperties)

-                         {

-                             message.AppendFormat("{0}={1}", param.Key, param.Value.ToString());

-                         }

-                         message.Append("}");

-                     }

-                     if (MyLogEntry.ReturnValue != null)

-                     {

-                         message.Append(";ReturnValue=");

-                         message.Append(MyLogEntry.ReturnValue.ToString());

-                     }

-                      if (MyLogEntry.Exception != null)

-                     {

-                         message.Append(";Exceptions=[");

-                         var exc = MyLogEntry.Exception;

-                         while (exc != null)

-                         {

-                             message.Append("{Message=");

-                              message.Append(exc.Message.Replace("\r", "\\r").Replace("\n", "\\n"));

-                             message.Append(";StakTrace=");

-                             message.Append(exc.StackTrace.Replace("\r", "\\r").Replace("\n", "\\n"));

-                             message.Append("},");

-                             exc = exc.InnerException;

-                         }

-                         message.Append("]");

-                     }

-                     return message.ToString();

-                 }

-             }

-          But here also , how do I pass my errorfault object?

2. Have

 

   public class MyLogEntry : LogEntry

   {

       public MyErrorFault myErrFlt//this is the extra information i want to log

       { get; set; }

   }

}

 

6.  [ConfigurationElementType(typeof(CustomHandlerData))]

public class MyExceptionHandler : IExceptionHandler

 

       #region Constructors

       public MyExceptionHandler(NameValueCollection ignore)

       {

 

       }

       #endregion

 

       #region IExceptionHandler Members

       public Exception HandleException(Exception exception, Guid handlingInstanceId)

       {

           // Perform processing here. The exception you return will be

           // passed to the next exception handler in the chain.

 

           if (exception is FaultException<MyErrorFault> )

           {

             MyErrorFault fault = (exception as FaultException<MyErrorFault>).Detail;

             if (fault.Type != null)

             {

                 if (fault.Type.Contains("MyServiceException"))

                 {

                     MyServiceException objMyBusinessEx = new MyServiceException("MyServiceException Type", exception, fault);

 

                     return objMyBusinessEx;

                 }

             }

MyLogEntry log = new MyLogEntry(); // I created a CustomLogEntry

 

                 log.myErrFlt = fault;

 

                 log.Message = exception.Message;

                 log.Message = fault.Message;

 

                 log.ExtendedProperties["FaultMessage"] = fault.Message;

                 log.ExtendedProperties["FaultExceptionDebug"] = fault.ExceptionDebug;

                 log.ExtendedProperties["FaultType"] = fault.Type;

                 log.Categories.Add("AuditFile");

 

                 log.Severity = System.Diagnostics.TraceEventType.Error;

 

                 foreach (DictionaryEntry dataEntry in exception.Data)

                 {

                     if (dataEntry.Key is string)

                     {

 

                         log.ExtendedProperties.Add(dataEntry.Key as string,

 

                         dataEntry.Value);

                     }

                 }

 

                 Logger.Write(log);

 

 

 

           }

 

 

           return exception;

       }

}

But in this case again my error fault details did not get logged

   <add name="MyServiceExceptionPolicy">

       <exceptionTypes>

         <add name="FaultException`1" type="System.ServiceModel.FaultException`1[[MyErrorFault, XYZ, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]], System.ServiceModel, Version=3.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.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

             logCategory="AuditFile"

               eventId="100"

               severity="Error"

               title="FaultException Details"

               formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"

               priority="0" />

             <add type="MyExceptionHandler, MyExceptions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

               name="MyExceptionHandler" />

             </exceptionHandlers>

         </add>

       </exceptionTypes>

Jul 28, 2010 at 2:42 AM
Edited Jul 28, 2010 at 3:14 AM

I think I have a simpler solution to this and I should have thought of it before.  It seems like your exception handler is only replacing the FaultException to an instance of MyServiceException.  You can do this by using the WrapHandler or ReplaceHandler.  The only other thing it seems to do is log the content of the MyErrorFault object.  You can do this by creating a class deriving from ExceptionFormatter or TextExceptionFormatter.  Override the WriteException method and create an exception that will contain all the details you want to be included in the Message property.  You can refer to the source code of the ExceptionFormatter and TextExceptionFormatter to get a better idea.  Your config then will have LoggingExceptionHandler which uses your custom exception formatter as its FormatterType and either a Wrap or ReplaceHandler (depending on what you want).

However, if the above approach doesn't suffice your need and you still need your MyExceptionHandler, here are the things which needs to be corrected:

First, in the code you posted above, you are using 2 custom log entries: MyMethodLogEntry and MyLogEntry.  In the custom exception handler, you created a MyLogEntry object which contains your MyErrorFault object.  However, in your custom formatter, you cast the LogEntry object to an instance of MyMethodLogEntry.  Are they really different or you simply mistyped it?  Anyway, what you should add is to cast the LogEntry object in the Format method of the custom formatter to whatever it is that you used in your custom exception handler containing your MyErrorFault object.  Add the logic of getting the MyErrorFault object from that custom log entry and append it to the message StringBuilder. 

Secondly, the config you posted indicates that logging will happen twice.  First, because you use the built-in logging exception handler.  Second, you also have your custom exception handler which also performs logging within its HandleException method.  Is this really your intention?  If not, you can remove the logging exception handler. 

Third, the MyLogEntry object you created inside the MyExceptionHandler doesn't specify to which category you want to log.  If you do this, it will log to the default category you specified in your loggingConfiguration section.  Specify a specific category  by adding this code:

log.Categories.Add("General");

Finally, make sure you used your MyLogFormatter as the Formatter of the trace listeners you added under the General category or to whatever category you used in your MyExceptionHandler.

Sarah Urmeneta
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com  

Jul 28, 2010 at 8:34 AM

Thanks for your reply

AvanadeSupport:First, in the code you posted above, you are using 2 custom log entries: MyMethodLogEntry and MyLogEntry.  In the custom exception handler, you created a MyLogEntry object which contains your MyErrorFault object.  However, in your custom formatter, you cast the LogEntry object to an instance of MyMethodLogEntry.  Are they really different or you simply mistyped it? 

Sonali: No these (1Adding a CustomLogFormatter and 2 MyLogentry)are the separately tried approaches...

AvanadeSupport:the MyLogEntry object you created inside the MyExceptionHandler doesn't specify to which category you want to log.  If you do this, it will log to the default category you specified in your loggingConfiguration section.  Specify a specific category  by adding this code:

log.Categories.Add("General");

Sonali:log.Categories.Add("AuditFile") has been already added..still it does not log

will try these and if it does not work will create a sample and send you across . Could you please let me know where to send it.

Thanks

Jul 28, 2010 at 8:39 AM

entlib.support@avanade.com

"log.Categories.Add("AuditFile") has been already added..still it does not log"

   - Make sure you used your custom log formatter as the Formatter of the trace listeners you added under the "AuditFile".

 

Sarah Urmeneta
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com  

Jul 28, 2010 at 8:40 AM

Also, I have forgot to mention that I have gone thru the example @http://customdatabasetracelistener.blogspot.com/ and tried to implement the same but getting some errors.

 and have requested for full sample 'pinkstarfish.305@gmail.com' but have not recieved it. 

Also, if you have working sample for the above thread Id - where a class is dering from LoggingExceptionHandler and implementing IExceptionHandler. Please send and notify me the same. my email id is sonali_lad2001@yahoo.com

Jul 28, 2010 at 8:48 AM

Why does not it log with TextFormatter

 

<loggingConfiguration

   name="Logging Application Block"

   tracingEnabled="true"

   defaultCategory="AuditFile"

   logWarningsWhenNoCategoriesMatch="true">

            <listeners>

                  <add

       name="Formatted EventLog TraceListener"

       type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FormattedEventLogTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

       listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FormattedEventLogTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

       source="MyApp"

       formatter="Text Formatter"

       log="MyApplication"

       machineName=""

       traceOutputOptions="LogicalOperationStack, DateTime, Timestamp, ProcessId, ThreadId, Callstack"/>

     <add

       name="Rolling Flat File Trace Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.RollingFlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

       listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.RollingFlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

       fileName="c:\temp\My.log" footer="---------------------------- End   Log ----------------------------"

       formatter="Text Formatter" header="---------------------------- Start Log ----------------------------"

       rollFileExistsBehavior="Increment"

       rollInterval="None"

       rollSizeKB="0"

       timeStampPattern="yyyy-MM-dd" />

   </listeners>

            <formatters>

                  <add

     type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

     template="Timestamp: {timestamp}&#xD;&#xA;Message: {message}&#xD;&#xA;Category: {category}&#xD;&#xA;Priority: {priority}&#xD;&#xA;EventId: {eventid}&#xD;&#xA;Severity: {severity}&#xD;&#xA;Title:{title}&#xD;&#xA;Machine: {machine}&#xD;&#xA;Application Domain: {appDomain}&#xD;&#xA;Process Id: {processId}&#xD;&#xA;Process Name: {processName}&#xD;&#xA;Win32 Thread Id: {win32ThreadId}&#xD;&#xA;Thread Name: {threadName}&#xD;&#xA;Extended Properties: {dictionary({key} - {value}&#xD;&#xA;)}"              

     name="Text Formatter"/>

   </formatters>

            <categorySources>

                  <add switchValue="All" name="AuditFile">

                        <listeners>

                              <add name="Rolling Flat File Trace Listener"/>

                        </listeners>

                  </add>

                  <add switchValue="All" name="EventLog">

                        <listeners>

                              <add name="Formatted EventLog TraceListener"/>

                        </listeners>

                  </add>

            </categorySources>

            <specialSources>

                  <allEvents switchValue="All" name="All Events"/>

                  <notProcessed switchValue="All" name="Unprocessed Category"/>

                  <errors switchValue="All" name="Logging Errors &amp; Warnings">

                        <listeners>

                              <add name="Formatted EventLog TraceListener"/>

                        </listeners>

                  </errors>

            </specialSources>

      </loggingConfiguration>

Jul 28, 2010 at 8:54 AM
Edited Jul 28, 2010 at 8:55 AM

My first impression when you said it did not log is that the contents of the MyErrorFault didn't get included in the log message.  Am I right or what happens is you didn't get any log at all?

 

Sarah Urmeneta
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com  

Jul 28, 2010 at 9:05 AM

Hi,

It worked with TextExceptionFormatter no need to have customlogFormatter. Surprisingly it logged  only

----------------------------Start Log ----------------------------
Timestamp: 27/07/2010 11:51:22
Message: The creator of this fault did not specify a Reason.
Category: AuditFile
Priority: -1
EventId: 0
Severity: Error
Title:
Machine: XYX
Application Domain: 6ee420cd-1-129247049780436181
Process Id: 7136
Process Name: C:\Program Files\Common Files\Microsoft Shared\DevServer\9.0\WebDev.WebServer.exe
Win32 Thread Id: 6980
Thread Name:
Extended Properties:
---------------------------- End   Log ----------------------------

anything yesterday when I tried..may be some issue with machine..

 

Now it is logging correct exception

---------------------------- Start Log ----------------------------
Timestamp: 28/07/2010 7:48:58
Message: Sequence contains no elements
Category: AuditFile
Priority: -1
EventId: 0
Severity: Error
Title:
Machine: XXX
Application Domain: 6ee420cd-1-129247764524257873
Process Id: 536
Process Name: C:\Program Files\Common Files\Microsoft Shared\DevServer\9.0\WebDev.WebServer.exe
Win32 Thread Id: 1480
Thread Name:
Extended Properties: FaultMessage - Sequence contains no elements
FaultExceptionDebug - System.InvalidOperationException: Sequence contains no elements
   at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source)
   at MyService.DetailOrder(xxxx)
   at <DynamicInterfaceHook_IOrderService>.DetailOrder(Order order, Corporation corp)
   at SyncInvokeDetailOrder(Object , Object[] , Object[] )
   at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
   at MyServices.ServiceModel.ServiceOperationHookBase.System.ServiceModel.Dispatcher.IOperationInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
   at MyServices.ServiceModel.ServiceOperationHookBase.System.ServiceModel.Dispatcher.IOperationInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
   at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
FaultType - System.InvalidOperationException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

---------------------------- AccorService:Aep End   Log ----------------------------

 

Thanks a lot. So please confirm if it is correct implementation

 [ConfigurationElementType(typeof(CustomHandlerData))]

public class MyExceptionHandler : IExceptionHandler

 

       #region Constructors

       public MyExceptionHandler(NameValueCollection ignore)

       {

 

       }

       #endregion

 

       #region IExceptionHandler Members

       public Exception HandleException(Exception exception, Guid handlingInstanceId)

       {

           // Perform processing here. The exception you return will be

           // passed to the next exception handler in the chain.

 

           if (exception is FaultException<MyErrorFault> )

           {

             MyErrorFault fault = (exception as FaultException<MyErrorFault>).Detail;

             if (fault.Type != null)

             {

                 if (fault.Type.Contains("MyServiceException"))

                 {

                     MyServiceException objMyBusinessEx = new MyServiceException("MyServiceException Type", exception, fault);

 

                     return objMyBusinessEx;

                 }

             }

MyLogEntry log = new MyLogEntry(); // I created a CustomLogEntry

 

                 log.myErrFlt = fault;

 

                 log.Message = exception.Message;

                 log.Message = fault.Message;

 

                 log.ExtendedProperties["FaultMessage"] = fault.Message;

                 log.ExtendedProperties["FaultExceptionDebug"] = fault.ExceptionDebug;

                 log.ExtendedProperties["FaultType"] = fault.Type;

                 log.Categories.Add("AuditFile");

 

                 log.Severity = System.Diagnostics.TraceEventType.Error;

 

                 foreach (DictionaryEntry dataEntry in exception.Data)

                 {

                     if (dataEntry.Key is string)

                     {

 

                         log.ExtendedProperties.Add(dataEntry.Key as string,

 

                         dataEntry.Value);

                     }

                 }

 

                 Logger.Write(log);

 

 

           }

 

 

           return exception;

       }

}

with LoggingHandler commented in ExceptionHAndler. So my ExceptionPolicy is just

<add name="MyServiceExceptionPolicy">

       <exceptionTypes>

         <add name="FaultException`1" type="System.ServiceModel.FaultException`1[[MyErrorFault, XYZ, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]], System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

           postHandlingAction="ThrowNewException">

           <exceptionHandlers>

<add type="MyExceptionHandler, MyExceptions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

               name="MyExceptionHandler" />

             </exceptionHandlers>

         </add>

       </exceptionTypes>

 

Thanks...

Can I request for 2 samples above for learning purposes mentioned in sonaliToday at 7:40 AM<abbr></abbr>

Jul 28, 2010 at 9:08 AM

Looks like I do not need those CustomeLogEntry object too.. Just adding them as extended properties has solved the problem..Is there anyway extended prioperties are configurable since I know the fault type and its members

Jul 28, 2010 at 9:16 AM

Yes, you can include it in the extended priorities, I should've suggested that first, I assumed you were planning to do something more complex besides on what you are tyring to solve right.  My mistake for assuming :)

Moving on to your question, what do you mean configurable?  I think what you want is to be able to correctly display whatever it is that you put in the ExtendedProperties.  Did you put the MyErrorFault object?  If you put any custom object in the extended properties, you'll simply get whatever is the result of their .ToString() method.  So in your case, simply override the ToString method in your MyErrorFault class and return whatever it is that you want to get included in the log message to represent an instance of that class.

Sarah Urmeneta
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com  

Jul 28, 2010 at 10:08 AM

No I can not modify MyErrorFault.

Also, when I tried to change the Category to EventLog ..nothing is getting logged in EventViewer?

public

 void ProcessContextItems(LogEntry log)

{

 

Hashtable contextItems = null;

 

// avoid retrieval if necessary permissions are not granted to the executing assembly

 

if (SecurityManager.IsGranted(new SecurityPermission(SecurityPermissionFlag.Infrastructure)))

{

 

try

{

contextItems = GetContextItems();

}

 

catch (SecurityException)

{

 

// ignore the security exception - no item could have been set if we get the exception here.

}

}

 

if (contextItems == null || contextItems.Count == 0)

{

 

return;

}

 

foreach (DictionaryEntry entry in contextItems)

{

 

string itemValue = GetContextItemValue(entry.Value);

log.ExtendedProperties.Add(entry.Key.ToString(), itemValue);

}

}

contextItems is null so it returns..

 

Jul 28, 2010 at 10:24 AM

Let's go back first on your custom exception handler.  You have a return line before you perform the logging; if fault.Type.Contains("MyServiceException"), you execute a return line of code and thus logging doesn't happen, is this really your intention?

Second, on your issue on not getting log when changing to EventLog category, let's examine your Formatted EventLog TraceListener defined in the config.  It's Log property is set to MyApplication and the Source property is set to MyApp.  Now, is MyApplication an existing event log in your machine? If yes, does the event source MyApp registered in MyApplication event log?  If not, execute this code to create the MyApp event source: (You must have admin privileges to do this)

EventLog.CreateEventSource("MyApp", "MyApplication");

Regarding on the contextItems, I'm not sure what is the relevance, where do you specifically use it in this scenario?

 

Sarah Urmeneta
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com