There's a lot of debate on the best type of error handling, where to log it (if at all), and when to do it. For example, here's a debate today:
http://weblogs.asp.net/alex_papadimoulis/archive/2005/03/29/396141.aspx
In this debate, the question is Return codes vs. Throwing an Exception. You can read the blog entry to see the debate. Here's my take, and why.
First, I always handle the exception where it happens, and rarely bubble it up. Why? First, what if the calling code doesn't handle the exception? Now you have an unhandled error, and it could cause an application to crash. On a recent project for a large chipmaking client, I got into this debate a few times with all levels of developers. It was always an interesting debate with senior developers because they at least adopted their belief into action and a bigger strategy. For example, they may trickle it up and handle it at the highest level, but if they forget to handle it, the best developers handle the uncaught errors at the thread level. The advantage is they have the error information and can use it to provide the user some feedback as to the nature of the problem.
But those are good developers, and I would say the top 10%. As an example, I had this debate with a mid-level developer who was convinced he was a senior level developer. Now, as I tell this story, let me mention I am a very good poker player. He adamantly felt errors should be bubbled up and handled at the UI. I countered him with the standard “What happens when the exception isn't handled?” He advised me he never forgets. Enter the poker player in me.
Being a consultant, you have to know when to shut up and let the “hand” play out. This was an XP project (never again), and as we paired, I watched him write some code, and I even reminded him about error handling. For the most part, with my diligence and because we had just had the discussion this guy was being fairly consistant in his exception handling at the UI level. However, I stopped reminding him (it's instinct for me to do it now), and sure enough, he forgot, and the application crashed a horrible death. The reason I wanted to have this discussion with him was because his code was causing about 80% of the crashes on the team, and I was trying to help him improve his skills (and our project). To finish the story, the guy's ego was so big he refused to accept responsibility for the crashes or even acknowledge that I had a valid point about forgetting error handling, and kept on his merry way.
Back to the point, IMHO error handling should be handled where the error occurs. This is especially true in multi-developer projects, as you cannot guarantee someone who calls your code is diligent enough to handle things when problems occur.
But that has a serious downside -- how do you let the user know there was a problem, and it was handled? The answer -- return values. OK, there's a little more to it than that. An error code is never enough information to provide feedback or to decide what to do when the caller receives notice an error occurred. Even worse, a number is useless in trying to relay relevant info to the end-user.
What I have adopted is this: I have a base class which my other classes inherit from. In that class, I have an ErrorMessage property (string) which is set where the error occurs, or in a central error handler. When a method encounters an error, it returns an integer value indicating failure. The calling routine (if desired) can check the ErrorMessage property and use that value for displaying a message or get details as to the nature of the problem. It can also ignore it if it wants. The key thing though is that the error will never go unhandled and cause the application to crash.
I'll be happy to post up some examples if there is interest. This is just a very important topic to me, and one I think which separates the wheat from the chaff, as they say!
In the debate listed above, there's an argument over performance. There's a balance here: you need to perform well, but performance is killed when the application crashes. Find a solution which is robust but also performs. Not handling an exception is not an option for robustness.