Wednesday, January 21, 2015

Handling Exceptions: Everything You Ever Wanted To Know

Exceptions have been around for a long time, but some seemingly experienced people still struggle with them. I will explain how to handle and communicate them in various .NET applications.

All exceptions in .NET inherit from System.Exception. You can't throw an instance of just any class; it must inherit from Exception. So, catching Exception means that nothing will get past your catch clause.

If there is a specific type of exception that you know you can handle, you must catch it before more general exception types. The class inheritance structure indicates which exceptions are more specific. ArgumentNullException inherits from ArgumentException, which inherits from Exception. So, if you want to handle the null exception differently from the argument exception, you must catch the null exception first. Each exception will be caught only once and by the first catch block that matches.

You can also add a "finally" clause after your catch clauses, in which you run code that must be run whether there was an error or not.

In the above examples and the following ones you'll see a variable called ex that holds the Exception object. You don't have to declare such a variable if you don't need a reference to it. Also, omitting the exception is the same as catch (Exception).


Default Exception Handling


Before we discuss how and when to handle exceptions, let's first look at how .NET handles them if our code doesn't.

Here is the code for a set of apps that throw exceptions in different ways: https://github.com/claq2/Exceptions

Console


Any exception in a console app, whether on the main thread, a new Thread/ThreadPool thread or started with the await keyword produces this:

Very straightforward, no ambiguous choices for the end user.

WinForms


When a WinForm application generates an exception on the UI thread that the code doesn't handle (e.g. in a button click event handler), .NET shows the user this:

What's a user to do? In my opinion, this is too much information for a non-developer to deal with, and the user has no idea what each choice's implications are. Do not rely on this dialog for end users. We'll see how to avoid this later.

If you start a thread, either using new Thread or ThreadPool.QueueUserWorkItem, and it generates an uncaught exception, .NET will terminate the application:

I have a Debug option here because Windows knows I have a debugger installed (Visual Studio). Again, we'll see how to prevent this later. The runtime will log this in the Application Event log along with the stack trace.

An exception thrown on a thread started with await results in the same prompt as the exception on the UI thread.

When you let .NET's manage your threads one thing it gives you is much better exception handling. Though again, the user must make an uninformed choice. The runtime doesn't add anything to the event logs because it handled the exception.

WebForms


An exception on the thread that is executing a web page request (e.g. in Page_Load or a button click event) shows:
Also known as "the yellow page of death". Only this request has come to an end. The system is still serving other requests. By default, when the request originates outside of the web server there is no stack trace giving away potentially sensitive information about the app. When viewed on the web server it will show more information. There is no event log entry because the runtime handled the exception.

The same thing happens when an uncaught exception is thrown on a thread started with await. Again, .NET handles it gracefully.

A truly insidious thing happens when a ThreadPool thread or one started with new Thread throws an unhandled exception: the .NET worker process dies. The symptom might be invisible to the user that caused it, but every request that w3wp.exe process was serving will come to an end. If the app uses in-process session state, those sessions will be lost. If the app uses Windows authentication, all of the auth sessions are gone and users will need to re-authenticate. (Ask me how I know! :) We'll see how to guard against this later. The runtime will record the exception along with the stack trace in the Application event log.

By default, the settings for an Application Pool (the thing the runs your ASP.NET website AKA the worker process w3wp.exe) will disable the pool if something kills the pool 5 times in 5 minutes. This is called Rapid-Fail Protection. When this kicks in, IIS returns HTTP 503, Service Unavailable for subsequent requests. If you find yourself having to disable this feature then I hope it's a short term mitigation while you hunt for the thing that's throwing exceptions on background threads.

WCF


An exception thrown on the main thread or an async thread sends an HTTP 500 error with this XML:


<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault>
<faultcode xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher">a:InternalServiceFault</faultcode>
<faultstring xml:lang="en-US">The server was unable to process the request due to an internal error.  For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the &lt;serviceDebug&gt; configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs.</faultstring>
</s:Fault>
</s:Body>

</s:Envelope>


The WCF proxy that Visual Studio/wsdl.exe generates throws a System.ServiceModel.FaultException. As the text above indicates, you can have the server send the server code's stack trace by changing a web.config setting. This might be acceptable in a development environment, but you don't want your service leaking implementation details in production.

Exceptions on background threads kill your ASP.NET worker process, w3wp.exe, just like WebForms. It's the same engine and threading mechanism. Again, the runtime records it in the Application event log.

MVC


Like WebForms, both exceptions on the thread processing a request and on a thread started with await result in an error page:
And like WebForms, an exception on a background thread (new Thread, ThreadPool) kills the w3wp.exe process serving the request, with all the same effects. This includes an Application event log.

WebAPI and HttpClient


WebAPI is very webby. Exceptions on the main thread and async threads that are not wrapped up into an HttpError and HttpResponseException result in an HTTP 500 Internal Server error and the following JSON:

{"Message":"An error has occurred."}

You can have WebAPI send more information by setting config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always in WebApiConfig.cs's Register method. With this enabled, an exception not thrown as an HttpResponseException will send:

{"Message":"An error has occurred.","ExceptionMessage":"Oh noooo!","ExceptionType":"System.Exception","StackTrace":"   at MVC.Controllers.ExceptionController.Get() in c:\\Users\\xyz\\Documents\\GitHub\\Exceptions\\MVC\\Controllers\\ExceptionController.cs:line 19\r\n   at ..."}

Oddly enough, wrapping up a caught exception in an HttpError and throwing HttpResponseException will cause the stack trace to be null.

There are 2 ways to detect an error with HttpClient: examine HttpResponseMessage.IsSuccessMessage or call HttpResponseMessage.EnsureSuccessStatusCode to get an HttpRequestException. EnsureSuccessStatusCode will not deserialize the above message. It's up to you to turn it into an object after determining that IsSuccessMessage is false. Here I've built my own class in a console application that has most of the same properties as HttpError, because HttpError lives in System.Web.Http.dll, which is part of the WebApi.Core package. I also needed a custom exception type so that I could specify the stack trace:

I understand that HttpClient is not specifically for interacting with WebAPI, but I am a little surprised that nothing natively converts WebAPI error responses into exceptions on the client side. ServiceStack built handy client and service components that share an error message format. This allows the client to reconstruct exceptions for you.

WPF


WPF applications have a consistent approach that I like. In all instances the application dies with this message:

Like a console application, there is no choice for the user. I like this because the state of the application is unambiguous.

Handling Exceptions


The general rule to catching exceptions is to catch only ones about which you can do something. For example, catch a timeout exception so that you can retry on behalf of the user. Or ignore an exception when deleting something that isn't there.

There are a few situations where you want to catch all exceptions:

  1. In background threads. As mentioned above, any unhandled exception in a background thread will kill your process, including your ASP.NET worker process.
  2. When you want to translate one application layer's exceptions or .NET exceptions into another layer's exception type.
  3. You want to throw a different exception in order to remove the stack trace.
  4. It's the last stop in your application's stack and you want to log the exception before letting it propagate up or out. For example Application_Error in Global.asax.cs.
  5. You want to handle all exceptions in a generic way instead of letting them reach the runtime engine.

"Safety Net" Pattern for Background Threads



I once saw this described as the safety net pattern, but I can't find the term in general use. You can see where it gets its name, though. Another name might be the Pokeman pattern, as in, you gotta catch 'em all.

Translating Exceptions



There are times you want to put some context around an exception for the next layer up so that they know which operation failed and whether there's something to be done about it. Include the current exception in the new one so that it gets put into the new exception's InnerException property. That way when it finally gets logged by something near the top of the call stack all of the error information will be present.

A similar approach is to purposely not include the current exception in the new exception. You want to do this when you want to be sure that potentially sensitive details don't escape a layer. In this case, you want to consider logging the inner exception before it is lost to upper layer logging.

Centralized Logging and Handling




Some systems, like ASP.NET WebForms/MVC and WebAPI provide a way to catch all unhanded errors, at which point you can log and transform the output. You can also perform extra steps like emailing support. This can remove an awful lot of try/catch and error logging code from individual methods.

Custom Exceptions


Before creating a custom exception or a set of them you need to think about what you want to get out of them. Generally, a custom exception communicates that you've reached a very particular condition that you want to identify from other conditions. That way you can log it and trace it back to a single line of code and potentially let users know what they need to do.

For example, business logic can be complex. It's usually a good idea to have a custom exception that represents a business layer failure, like not providing a needed bit of data. It's not enough, though, to throw this single exception type for every rule failure. You could hardcode the message to show e.g. "No IP address specified", "Invalid IP address specified", but then your app is limited to one language of user. (If you've ever used FxCop or Visual Studio's Code Analysis then you are probably familiar with the "move string to resource file" warning :)

What's needed is one of the following:

  1. A collection of specific custom exceptions all inheriting from a layer-specific parent exception class. The parent class is for the convenience of the next layer up so that it can catch all layer-specific exceptions and handle them differently from other libraries' errors or .NET's.
  2. A single layer-specific parent exception and a collection of error codes/enum values, each representing a particular condition.
An app can then map the collection of exceptions or the error codes/enum values to matching culture-specific strings. .NET provides Resource classes for this scenario.

If you use the error code/enum approach, put the value in the Data dictionary property that is part of the .NET Exception class. This is effectively a dictionary of <Object, Object> into which you can stuff whatever you like in order to send values up the call stack. Don't add properties to your custom exception. You may find yourself having to hand your custom exception to some generic handler that won't know about your properties. It will also save having to perform a slightly expensive cast to your custom type to get the property. Generic handlers will only need to query the Data property for the error code. Keep the key in a public static property.


The alternative is an exception for each error instead of an enum value. Each exception should look up its own error message from a culture-specific Resource class. This approach is more in line with how .NET does things. Some drawbacks are the extra code and that the exceptions will only be understood by other .NET or SOAP-based systems. There is no standard way to send exceptions over, say, JSON.

This way you can send a language-specific message to the user and log in another language.

Anti-patterns and Common Mistakes


Catch-Log-Throw


Doing this all the way up the call stack will fill your logs with the same error again and again, each time adding another call to the stack. This makes logs much harder to read. Did the same error really happen just once? How do you accurately count the number of errors?

Do centralize your logging and stick to the strategy. In the above case you would remove the try and the catch block. Just let the exceptions propagate up the stack.

Throw ex


To propagate a caught exception up the stack as-is, just "throw". When you "throw ex", you create a new exception with the same type as the caught one, but the call stack in the exception starts from where you threw it. The stack trace from the one that you caught is no longer available to upper layers. I don't know of a good reason to "throw ex". I've only ever seen it lead to poor debugging sessions and confusing logs.

Complex Exception Families


Don't group your custom exceptions under other custom exceptions. For instance, if you have a SaveCustomerException and a LoadCustomerException, don't make them inherit from CustomerOperationException that inherits from a base CustomException class. It's needlessly complicated. No code needs to catch this vs. the specific exception or the base custom one.

The worst approach I've seen is a combination of custom exceptions that each have their own embedded enum of reasons. Translating these into a single custom exception and single enum value ends up looking like this:

Now every time there is a new exception reason in one of those enums this method needs another case statement. If there is a new class of exception to catch then we need a  A total waste of time.

Not Using Exception.Data


I've never seen .NET use the Data property, but it is the best place to put custom information, rather than adding properties to your custom exceptions. If you feel you must use custom properties, use Data as a backing store so that it is available without casting to your custom type.

No comments:

Post a Comment