Using Exception.Data to Append Extra Data to Your Exception

Technorati Tags:

This is one of the rare times I’ve needed to append some data to an Exception.  And I really hadn’t used Exception.Data before.  So today was interesting.  I was tired of this third party API causing me inconsistent results when we call methods (because the API is not very reliable in the first place) so I wanted to log the SOAP request that’s being sent every time as part of any logging/debugging which is very useful when you only have just the .NET exception stack and really are missing the other piece of the puzzle, which is what was the SOAP data that was sent over the HttpRequest when you got the error in the first place.

But if you’re handling exceptions correctly and logging them somewhere you should be able to get enough information from your logging to figure out what went wrong in most cases.

Here’s an example of how I used Exception.Data today.

1) I’ve got a Request.cs abstract class

     In it, I’ve got the work of setting up and sending the actual HttpRequest via this method:

public Response.Response SendRequest()
{
    Stream requestStream;
    Stream responseStream;
 
    XmlDocument doc = new XmlDocument();
    doc = CreateRequestXML();
 
    // Grab the SOAP that is being sent for possible logging later
    _SOAPSent = doc.OuterXml;
 
    // Determines if API call needs to use a session based URI
    string requestURI = UseAuthURI == true ? _requestURIAuthBased + SessionID : _requestURI;
 
    byte[] data = XmlUtil.DocumentToBytes(doc);
    HttpWebResponse response = null;
 
    // Create the atual Request instance
    HttpWebRequest request = CreateWebRequest(requestURI, data.Length);
 
    request.ContentLength = data.Length;
    request.KeepAlive = false;
    request.Timeout = 30000;
 
    try
    {
        // Send the Request
        requestStream = request.GetRequestStream();
        requestStream.Write(data, 0, data.Length);
        requestStream.Close();
 
        // get response
        response = (HttpWebResponse)request.GetResponse();
    }
    catch (Exception ex)
    {
        // include SOAP string that was sent
        ex.Data.Add(Enums.ExceptionDataRequest.SOAPCalled, _SOAPSent);
       
        //Throw to retain data
        throw;
    }
 
    ....rest of this returns a response back
}

Notice these lines above:

   _SOAPSent = doc.OuterXml;
        // include SOAP string that was sent
        ex.Data.Add(Enums.ExceptionDataRequest.SOAPCalled, _SOAPSent);
        //Throw to retain data
        throw;

So I catch the exception and also appending some data to it using Exception.Data.   What I added to it was the SOAP text that was sent in the HttpRequest.

Now here’s what you can’t forget to do.  You need to re-throw the exception to retain it as I did above or else you’ll lose that data.

2) So now in my code-behind of one of my .aspx I have the following:

private void SomeMethodInCodeBehind()
{
    ...
    try
    {
        xxx.UpdateFrequency(...);
    }
    catch (Exception ex)
    {
        service.LogFailure(..., ex.Data[Enums.ExceptionDataRequest.SOAPCalled].ToString());
    }
       
    Response.Redirect("somepage.aspx?freq=1&action=" + _actionID);
}

So here I’m logging the exception and then retrieving that SOAP string that was sent to also log that:

ex.Data[Enums.ExceptionDataRequest.SOAPCalled].ToString()

3) xxx.UpdateFrequency, the method that I called from code-behind looks like this:

public void xxxFrequency(string email, string actionID, string listID)
{
    ...
    UpdateFrequencyRequest request = new UpdateFrequencyRequest(actionID, email, listID);
    UpdateFrequencyResponse response = (UpdateFrequencyResponse)request.SendRequest();
    ...
}
So ultimately this is being wrapped in that try/catch in my code-behind.  I make the request and am calling SendRequest().  When the request fails, it will throw that error up the stack until it hits my catch statement in my code-behind in #2.  Then I can easily grab the SOAP that was called during that failure.

Now I can log the SOAP anytime I call the request class’s SendRequest easily throughout my code and get something such as this to log and use for debugging if needed:

<Envelope>
  <Body>
    <SelectRecipient>
      <LIST_ID>11111</LIST_ID>
      <EMAIL>someemail@domain.com</EMAIL>
    </SelectRecipient>
  </Body>
</Envelope>
 
 
Notice that I also setup an Enum so that I don’t have any magic strings laying around when I’m accessing the keys from the data dictionary.

Pretty cool stuff, common, just never used the data dictionary on the exception object much but definitely powerful.


Print | posted on Thursday, September 10, 2009 8:14 PM

Comments on this post

# re: Using Exception.Data to Append Data to Error Stack

Requesting Gravatar...
Exception.Data is not special. You don't lose it because you don't rethrow your exception. Exceptions are .NET objects allocated on the heap just like anything else. The behavior you were experiencing was that you caught and swallowed the first instance of your exception, most likely some FaultException type from WCF. You added to the Data dictionary of that exception instance, and then you lose all references to that instance the moment the code leaves the catch block. I'm not sure how you were getting an Exception further up your call stack if you caught and swallowed the first one. Perhaps it was a NullReferenceException or something else indicative of normal code failure. Basically it was not the same exception instance, let alone type, as the one you originally caught and added to its Data dictionary. Without the rethrow behavior at the original try/catch block, the code would flow past the end of the catch block and some code further down the line would most likely throw a seemingly unrelated exception, which you assumed would be the one with the modified Data dictionary you were hoping to somehow magically re-catch. I think you need to read up some more on how exceptions actually work.
Left by Mysterious Stranger on Sep 10, 2009 10:48 PM

# re: Using Exception.Data to Append Data to Error Stack

Requesting Gravatar...
Sorry stranger but YOU are wrong in this case.  First off you don't know my intent.  Further up the line I want to capture ANY exception from that called method in Request.cs.  So if there was another exception being thrown later, that's just fine.  I am not catching a specific Exception type so your argument is not related to my original intent which I did not specify so you're assuming that here.

Be careful when calling someone out especially the way you did at the end of your little sophisticated paragraph and especially before you doing your own homework, it sort of pisses someone off when you come off with an egoistical naive reply like this with fancy technical terms that don't amount to a correct accusation.  While I'm very open to people with better ideas or telling me I'm wrong (if you knew me, you'd know this is very true), I don't think you have a case here so I think it's my duty to put the facts up.


Using throw; throws the exact same exception that was caught again so that it is NOT swallowed and CAN be handled and the same exception and data caught futher up in classes calling this function as long as another exception was not thrown again as you stated.   So yes, I handled it and then rethrew it which DID do the job and did do 2 things which IS how exception handling works and I handled this properly:

1) rethrows the same exception thus the same call stack (preserved)
2) since I added data to that exception, rethrowing it just passes on the exception object (yes on the heap) and its data up to any functions higher up that want to catch it again

So I don't get where you are telling me to look this up...your reply doesn't make sense to me.  I think you might need to look it up. 

What I did miss though is that I needed to add a check to see if that Exception.Data value even exists in the dictionary.  Because if there was a different exception thrown other than the one I threw, it would not have the data I appended as it would be an entirely different Exception instance.  So I can do a simple check using an extension method I created to return a string back if that key is not in that data dictionary.  Otherwise, I don't really care.  Either that key is there or it's not, same exception or not, I needed to check this so that was indeed missing.

May I recommend a good book to you which talks just about this very clearly:

Programming C#: Building .NET Applications with C#


In fact here's a very good sentence out of that book to help you:

p. 280 "You might want your catch block to take some initial corrective action and then rethrow the exception to an outer try catch block (in a calling function).  It might rethrow the same exception, or it might throw a different one".

That's exactly what I did.  I re-threw the exception (using just throw;) rather than throwing a complete new/different exception (Exception newEx = new Exception..blah blah blah throw newEx).  Therefore that's why later I could use yet another try/catch to grab that rethrown exception and still get that data and call stack...because I threw the SAME exception that was caught again.   Again, if something intercepts that and overrides such as another exception is thrown after I rethrew this one, that's not something I care about or can control and in fact I don't care because I'm capturing ANY exception in my code-behind that happens with this method call so it's all good for my intent. 


>>most likely some FaultException type from WCF

what does WCF have to do with this?

>>I'm not sure how you were getting an Exception further up your call stack if you caught and swallowed the first one. Perhaps it was a NullReferenceException or something else indicative of normal code failure.

Because I re-threw it!  throw;  that's how.  And no it was not another exception type.  It was the same one.  Adding throw allowed me to retain the data.  It was not that a different type of exception got thrown outside of my catch block somewhere else before it hit my 2nd catch block.

I was getting an error that the request failed.  I had changed the Uri path and expected it to fail when I forced the failure.  So no, I was not getting a default system error, it was specific to the API I'm using and the 3rd party server that I was specifiying was wrong thus forcing an exception telling me that it could not communicate to that Uri.  So YES I was getting the error I expected when forced and not this mysterious idea you talk about.

In fact to prove you wrong, I'll show you the actual exception I got back which is what I expected when force testing this:

System.Net.WebException: The remote name could not be resolved: 'api10.bbb.com'

This is not a default exception from the System.Exception, this is a very sepecific HttpRequest exception saying that it cannot find that remote server! 

So, unless you can back up your statement, it's null....Check and ask for my intent first before making accusations.
Left by Dave Schinkel on Sep 10, 2009 10:56 PM

# re: Using Exception.Data to Append Extra Data to Your Exception

Requesting Gravatar...
Rethrowing exceptions is .net 101 "mysterious stranger"...
I never thought to append to the data property of the exception though( i usually just use an xml wrapper node with added details about the environment and current users.) I'll definitely be looking into providing more verbose error details right from the source of the error now.

Thanks
Left by DtrewG on Sep 14, 2009 10:00 AM

Your comment:

 (will show your gravatar)
 
Please add 6 and 4 and type the answer here: