I have been a designer and coder of safety-critical systems for many years, and key to that type of system is robustness, one aspect of which is exception handling.
Some basic best practices:
1) At any level, catch only those exceptions you can deal with then and there. To "deal with" almost always means to retry or give up what you were trying to do, and that usually means that you should catch the exception one level up from where the exception-throwing call is issued.
2) Catch exactly those exceptions listed by the API documentation, no more, no less. Even if there are five exceptions listed for a call, don't catch their common base class (if they have one). However, you don't always need to catch all exceptions from the same call in the same place.
3) Don't pass nitty-gritty exceptions up to a level where they carry no meaningful information. Instead, catch the exception at the low level, log it if appropriate and return a status code or throw a more generic exception to the caller above.
4) Only use (and do use) catch-all handlers when you are in a critical section. Catch the exception, relinquish the resource (semaphore or whatever), rethrow the exception.
5) Logging an exception is not handling it. If all you do in a particular handler is log the exception, you should probably get rid of that handler.
6) Be very careful with exception handlers in loops, especially loops without a delay or a way out if the exception is thrown. You can easily get stuck in a busy loop that way.