Exception Handling in Services

Topics: Exception Handling Application Block
Mar 1, 2011 at 3:22 PM

OK, first post and am very new to the Enterprise library. The brief for the application is that we want to convert system errors/exceptions into more friendly errors, but also log the exception details into a log file. Each control event in out web front-end uses a try/catch block, with the catch calling a HandleExp method which works with the errors and deals with them accordingly. alongside this application, we also have about 6 services - does any of the sections from web.config need to be applied to the service app.config? We don't want the service errors being made friendly during development, but also am unsure why we are currently getting back errors from the appropriate policy before the errors are handled by the front end.

My web.config section is below - is there anything obviously wrong? It does load into the editor without any problems, but am not sure if the priority and/or event IDs are correct. They also seem to be missing handler attributes, but am not sure if these are needed or not?

<configSections>
    <section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
    <section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
    <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
</configSections>

  <loggingConfiguration name="Logging Application Block" tracingEnabled="true" defaultCategory="Errors" logWarningsWhenNoCategoriesMatch="true">
    
    <listeners>
      <add name="Error Log Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        fileName="error.log" header="------------------Start Log------------------"
        footer="-------------------End Log-------------------" formatter="Text Formatter"
        traceOutputOptions="None" filter="All" />
    </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;&#xD;&#xA;           Message: {message}&#xD;&#xA;&#xD;&#xA;           Category: {category}&#xD;&#xA;&#xD;&#xA;           Priority: {priority}&#xD;&#xA;&#xD;&#xA;           EventId: {eventid}&#xD;&#xA;&#xD;&#xA;           Severity: {severity}&#xD;&#xA;&#xD;&#xA;           Title: {title}&#xD;&#xA;&#xD;&#xA;           Machine: {machine}&#xD;&#xA;&#xD;&#xA;           Application Domain: {appDomain}&#xD;&#xA;&#xD;&#xA;           Process Id: {processId}&#xD;&#xA;&#xD;&#xA;           Process Name: {processName}&#xD;&#xA;&#xD;&#xA;           Win32 Thread Id: {win32ThreadId}&#xD;&#xA;&#xD;&#xA;           Thread Name: {threadName}&#xD;&#xA;&#xD;&#xA;           Extended Properties: {dictionary({key} - {value}&#xD;&#xA;)}"
        name="Text Formatter" />
    </formatters>
    
    <categorySources>
      <add switchValue="Error" name="Errors">
        <listeners>
          <add name="Error Log Listener" />
        </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="Error Log Listener" />
        </listeners>
      </errors>
    </specialSources>
  </loggingConfiguration>
  
  <exceptionHandling>
    <exceptionPolicies>
      
      <add name="BusinessLogicLayerPolicy">
        <exceptionTypes>
          <add name="DomainInternalMessageException" type="ALT_Shared.Exceptions.DomainInternalMessageException, ALT_Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
            postHandlingAction="ThrowNewException">
            <exceptionHandlers>
              <add name="Internal Message Exception" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                logCategory="Errors" eventId="200" severity="Error" title="Enterprise Library Exception Handling"
                formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                priority="0" useDefaultLogger="true" />
              <add type="ALT_Shared.ExceptionHandlers.DomainInternalMessageExceptionHandler, ALT_Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                name="Internal Message Exception Handler" />
            </exceptionHandlers>
          </add>
          <add name="EntityValidationException" type="ALT_Shared.Exceptions.EntityValidationException, ALT_Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
            postHandlingAction="ThrowNewException">
            <exceptionHandlers>
              <add name="Log Entity Validation Exception" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                logCategory="Errors" eventId="200" severity="Error" title="Enterprise Library Exception Handling"
                formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                priority="0" useDefaultLogger="true" />
              <add type="ALT_Shared.ExceptionHandlers.EntityValidationExceptionHandler, ALT_Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                name="Entity Validation Exception Handler" />
            </exceptionHandlers>
          </add>
          <add name="Exception" type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="ThrowNewException">
            <exceptionHandlers>
              <add name="Log System Exception" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                logCategory="Errors" eventId="200" severity="Error" title="Enterprise Library Exception Handling"
                formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                priority="0" useDefaultLogger="true" />
              <add type="ALT_Shared.ExceptionHandlers.DomainExceptionHandler, ALT_Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                name="System Exception Handler" />
            </exceptionHandlers>
          </add>
        </exceptionTypes>
      </add>
      
      <add name="DataAccessLayerPolicy">
        <exceptionTypes>
          <add name="DomainInternalMessageException" type="ALT_Shared.Exceptions.DomainInternalMessageException, ALT_Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
            postHandlingAction="ThrowNewException">
            <exceptionHandlers>
              <add name="Internal Message Exception" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                logCategory="Errors" eventId="200" severity="Error" title="Enterprise Library Exception Handling"
                formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                priority="0" useDefaultLogger="true" />
              <add type="ALT_Shared.ExceptionHandlers.DomainInternalMessageExceptionHandler, ALT_Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                name="Internal Message Exception Handler" />
            </exceptionHandlers>
          </add>
          <add name="EntityValidationException" type="ALT_Shared.Exceptions.EntityValidationException, ALT_Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
            postHandlingAction="ThrowNewException">
            <exceptionHandlers>
              <add name="Entity Validation Exception" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                logCategory="Errors" eventId="200" severity="Error" title="Enterprise Library Exception Handling"
                formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                priority="0" useDefaultLogger="true" />
              <add type="ALT_Shared.ExceptionHandlers.EntityValidationExceptionHandler, ALT_Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                name="Entity Validation Exception Handler" />
            </exceptionHandlers>
          </add>
          <add name="OptimisticConcurrencyException" type="System.Data.OptimisticConcurrencyException, System.Data.Entity, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="ThrowNewException">
            <exceptionHandlers>
              <add name="Log Optimistic Concurrency Exception" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                logCategory="Errors" eventId="200" severity="Error" title="Enterprise Library Exception Handling"
                formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                priority="0" useDefaultLogger="true" />
              <add type="ALT_Shared.ExceptionHandlers.OptimisticConcurrencyExceptionHandler, ALT_Shared, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
                name="Optimistic Concurrency Exception Handler" />
            </exceptionHandlers>
          </add>
          <add name="SqlDatabaseException" type="System.Data.SqlClient.SqlException, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="ThrowNewException">
            <exceptionHandlers>
              <add name="Log Sql Exception" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                logCategory="Errors" eventId="100" severity="Error" title="Enterprise Library Exception Handling"
                formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                priority="0" useDefaultLogger="true" />
              <add type="ALT_Shared.ExceptionHandlers.DomainExceptionHandler, ALT_Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                name="Sql Exception Handler" />
            </exceptionHandlers>
          </add>
          <add name="Exception" type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="ThrowNewException">
            <exceptionHandlers>
              <add name="Log System Exception" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                logCategory="Errors" eventId="200" severity="Error" title="Enterprise Library Exception Handling"
                formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                priority="0" useDefaultLogger="true" />
              <add type="ALT_Shared.ExceptionHandlers.DomainExceptionHandler, ALT_Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                name="System Exception Handler" />
            </exceptionHandlers>
          </add>
        </exceptionTypes>
      </add>
      
    </exceptionPolicies>
  </exceptionHandling>

Am also getting the following error from one of the services, and wanted to find out if anything is needed in the app.config to allow the error to be managed correctly:

Exception: Activation error occured while trying to get instance of type ExceptionPolicyImpl, key "BusinessLogicLayerPolicy"

Thanks in advance

Martin

Mar 2, 2011 at 6:17 AM

With regards to the error you're getting, it occurs if the corresponding exception policy name specified doesn't exists in the config file or if the config file itself cannot be found. Where is the try catch block with the call to HandleException is?  Is it on the service or on the client?  If it's on the service side, putting a try catch block isn't necessary.  If you're service is going to use a single exception policy, you can put an ExceptionShielding attribute to the service specifying the name of the exception policy you want to use.

[ExceptionShielding("BusinessLogicLayerPolicy")]

Could you also clarify this statement further? "but also am unsure why we are currently getting back errors from the appropriate policy before the errors are handled by the front end"

You can also check out this blog first on how to use EHAB for implementing WCF exception shielding.

 

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

Mar 2, 2011 at 8:42 AM

Thanks Sarah

At present, each event procedure (page_load, button click etc) on the web client uses a try/catch block as follows:

try
{
        // relevant code...
}
catch (Exception ex)
{
        this.HandleExp(ex);
}        

The procedure HandleExp is as follows:

        public void HandleExp(Exception exception)
        {          
            // if exceptiopn not found...
            if (exception == null)
            {
                // simply redirected to a new page...
                return;
            }

            // define which type of exception occurred and deal with accordingly...
            if (exception is System.Threading.ThreadAbortException)
            {
                return;
            }

            // get the exception type...
            var expType = exception.GetType();

            // define which type of exception occurred and deal with accordingly...
            if (expType == typeof(System.Threading.ThreadAbortException))
            {
                return;
            }

            // define variable to hold exceptions...
            var exceptionMessage = new StringBuilder();

            if (expType == typeof(EntityValidationException) || expType == typeof(ValidationException) || expType == typeof(DomainSecurityException))
            {
                // get the message text...
                string messageText = exception.InnerException == null ? exception.Message : exception.InnerException.Message;

                // append it to the stringbuilder...
                exceptionMessage.Append(messageText);
            }       
            else if (expType == typeof(EntityValidationException))
            {
                // These are the type of errors generated when we add additional validation...
                var entityValidationException = (EntityValidationException)exception;
                exceptionMessage.Append(
                    ValidationResultsFormatter.GetConcatenatedErrorMessages(
                        entityValidationException.ValidationDetails, "\\n", true));
            }
            else if (expType == typeof(DomainInternalMessageException))
            {
                // These are the type of errors generated a System.Exception occurs and is
                // converted by the exception handling policy to a more friendly format
                var domainInternalMessageException = (DomainInternalMessageException)exception;
                
                if (domainInternalMessageException.MessageSeverity == InternalMessageSeverityType.Warning)
                {
                    exceptionMessage.Append(domainInternalMessageException.ExceptionMessage);
                }
                else
                {
                    this.DisplayErrorPage(domainInternalMessageException.ExceptionMessage);
                }
            }
            else
            {
                exceptionMessage.AppendFormat(ErrorMessagesRes.Standard_Error_Format, "Unknown error", exception.InnerException.Message);                
            }

            // dispaly the message...
            this.DisplayJavascriptMessage(exceptionMessage.ToString());
        }

As you can see, it generates text to either display as an alert dialog, or pass the user to a generic error page. What i meant by my earlier comment was - if you place a breakpoint on the line "this.HandleExp(ex);", the exception is already defined as the description from the BusinessLayerPolicy. For developing, this is unhelpful, so really wanted to know if it's possible to switch off the policy handling during development. The service code has a try/catch block, and the catch simply throws the exception.

I hope this makes sense, as am new to this,  and the code I started with was added in by a developer who had some knowledge already. I am left trying to figure out how to get it working.

Thanks

Martin

 

Mar 2, 2011 at 9:38 AM

I see, so in your case you intend to implement the exception policies on the client and not on the server, am I right?  If this is the case, then your exception policies should be defined in the configuration file of the client project and not in the configuration file of the wcf service project.

In addition, if a WCF service throws an exception that exception will be converted to a FaultException.  If you set the includeExceptionDetailInFaults attribute to true of the serviceDebug element, you will be able to get the actual error message but if you set it to false, you will get the unfriendly error message "The server was unable to process the request due to an internal error....".

So as you can see, your code HandleExp code won't work.  I'm not aware if there's any way of getting the actual exception type that was thrown in the service, I suggest you post it in a WCF forum.

You might also want to consider the process of handling exceptions in your application, I suggest you check out Exception Shielding as this is a suggested best practice for error handling in WCF.

 

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

Mar 2, 2011 at 10:01 AM

Thanks. The policies above are only defined in web.config for the application, but not in any of the service's app.config files. I think I need to understand this so much more!

Thanks for the hint about the attribute - will try and figure out where to set that and see if that helps.

Martin

Mar 2, 2011 at 10:23 AM

Also, the services (created as such in VS2008) are installed onto a remote machine as the run using polling. They are not called by the web application, and so any errors will not be passed through to the front end.

I hope this helps

Martin

Mar 2, 2011 at 10:26 AM

You're welcome, glad to be of help :)

Ok, so your exception policies are defined in the correct configuration.  And to clarify, you'll always get a FaultException in the client whether you set the includeExceptionDetailInFaults attribute to true or false.  The only difference is the error message you'll get.

As for the whole exception handling process, I think you need to be sure that the conversion of the error message should be done on the client side and if no error handling should be done in the service.   

 

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

Mar 2, 2011 at 11:17 AM

Thats one good thing then :0).

Can you please clarify - does the system.servicemodel section go into web.config for my client facing application to allow the messages shown during debugging more useful?

As I understand it, the services currently write straight to the application log on the remote machine, and so have no need for the policies - I am not sure if this is the 'right' way to do that, but it's not an element of the development I have been involved with until now.

The services are there to populate data on a daily/user interaction basis, and the front end will deal with the data not being available from these services accordingly, so think that the logic is probably OK. Maybe not ideal, but with less than 1 month to release....

Thanks again

Martin

Mar 3, 2011 at 7:24 AM

I'm afraid we're a bit confused with your inquiry. Could you provide more details regarding this and what exactly is the entlib related problem you're facing. Thanks.

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

Mar 3, 2011 at 8:20 AM

Hi

Earlier, in one of Sarah's replies, she mentioned using includeExceptionDetailInFaults to allow more detail to be displayed in the errors whilst developing. I did some googling, and it seemed to imply that I needed to add a system.servicemodel section to my config file, as this is where the particular attribute would go. I want to be sure that, at least during development/debugging, any error message returned contains sufficient information.

Thanks

Martin

Mar 3, 2011 at 9:08 AM

Hello Martin,

Ah okay, so this is more of on the WCF Service side related. By default if you create a WCF Service App this attribute is already available and no need to add it manually. 

If you would want to dig deeper on this I would suggest posting this in a WCF Forum to get better answer.

Though, I'm still unsure what is the specific entlib problem you're facing. Are you using Entlib EHAB's WCF Exception Shielding or Exception Handling in your client side app?

Looking at the code you posted, I actually haven't found any line of code that handles the exception via EHAB or am I just missing the whole picture here.

As what you've mentioned from your earlier post what you want to achieve is this 

"The brief for the application is that we want to convert system errors/exceptions into more friendly errors, but also log the exception details into a log file." ,

what I can suggest to get you started on this is you can refer to the documentation http://msdn.microsoft.com/en-us/library/ff664352%28v=PandP.50%29.aspx. HTH :o)

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

 

Mar 3, 2011 at 9:35 AM

Thanks - will follow the link you provided, and come back if I have any further questions.

Martin