Almost five years ago I posted this "Good Exception Management Rules of Thumb" and had some interesting and useful comments. As with all lists of rules of thumbs, they may no longer be valid. What do you think?
Cori Drew commented to me in an email that she often sees code like this in the wild (changed to protect the innocent 3rd parties). This kind of code inevitably shows up in a file called Utils.cs, which may be your first clue there's trouble.
public void HandleException(String strMessage){ //Log to trace file only if app settings are true if (Properties.Settings.Default.TraceLogging == true) { try { TraceSource objTrace = new TraceSource("YadaYadaTraceSource"); objTrace.TraceEvent(TraceEventType.Information, 5, strMessage.ToUpper()); objTrace.Flush(); objTrace.Close(); } catch { //do nothing if there was an error } } throw new Exception(Environment.NewLine + strMessage);}
What's wrong with this code, Dear Reader?
There's a number of things that we can learn from, so have at it in the comments in nice lists bulleted with asterisk, won't you? You can comment on lines, or the larger strategy or both. I'll update the post with a roll-up once you come up for breath.
public static void HandleException(Exception exception, HttpContext context) { HttpException httpException = exception as HttpException; if (httpException != null && (httpException.GetHttpCode() == 405 || httpException.GetHttpCode() == 404 || httpException.GetHttpCode() == 403)) { context.Response.StatusCode = httpException.GetHttpCode(); context.Response.SuppressContent = true; context.Response.End(); return; } SmtpClient mailClient = new SmtpClient(System.Configuration.ConfigurationManager.AppSettings["MailServer"]); StringBuilder errorMessage = new StringBuilder(); if (exception is HttpUnhandledException) exception = exception.InnerException; errorMessage.Append(exception.ToString()); if (httpException != null) errorMessage.Append("\n\nHTTP EXCEPTION CODE: " + httpException.GetHttpCode()); if (exception.InnerException != null) { errorMessage.Append("\n\n ***INNER EXCEPTION*** \n"); errorMessage.Append(exception.InnerException.ToString()); } if (context != null) { //if (context.Request.IsLocal) // return; errorMessage.AppendFormat("\n\nRequest Path = {0}\n", context.Request.Url); errorMessage.Append("\n\n ***REQUEST PARAMETERS*** \n"); foreach (string LName in context.Request.Params.Keys) { errorMessage.AppendFormat("\n{0} = {1};", LName, context.Request[LName]); } if (context.Session != null) { errorMessage.Append("\n\n ***SESSION VARIABLES*** \n"); foreach (string key in context.Session.Keys) { errorMessage.AppendFormat("\n{0} = {1};", key, context.Session[key]); } } } System.Diagnostics.Debug.Print(errorMessage.ToString()); try { mailClient.Send ( SiteName + " Web Server <server@site.com>", System.Configuration.ConfigurationManager.AppSettings["ErrorEmail"], SiteName + " Error " + Guid.NewGuid().ToString(), errorMessage.ToString() ); } catch (System.Net.Mail.SmtpException) { } }
if (Properties.Settings.Default.TraceLoggingEnabled == true)
if (Properties.Settings.Default.TraceLogging)
objTrace.TraceEvent(TraceEventType.Information, 5,....
* 1. Rename the Method. Why is the message just a string? Parameter should not be string. Parameter name should not have str* 3. Comment is not required based on the next line being readable.* 4. Possible null exceptions? == true is not required (but not a showstopper!).* 8. Could use var. Poor variable name. TraceName should be a constant/readonly property.* 9. Why is the event type info, shouldn't it be an error? What is 5? Why is the message upper? (We hope its not a "business requirement!")*10. Should this be done with a 'using' block?*13. Should we only catch exceptions we want?*15. It might be better to let the trace error bubble up*18. Shouldn't be using the generic Exception. Why to we add a new line?
* Worth running FxCop/ReSharper* Hard to tell how wrong it is without more context.* How testable is this? Should the TraceSource be created via IoC/Injection?* Should the Trace section be pushed into a different method/class? (SRP)* Should Elmah be used?
public static T EatTheException<T>(Func<T> action, string actionDescription, out bool success, out Exception ex) { ex = null; try { T result = action(); success = true; return result; } catch (Exception exception) { ex = exception; Trace.WriteLine("Erorr occured while " + actionDescription + ": " + ex.Message); } success = false; return default(T); }
* You should really only "handle (catch)" exceptions if you can do something about the root cause. Logging isn't the same has handling. * Don't catch everything, catch specifics you can deal with. Catch DataException or ReadOnlyException, never ApplicationException or just Exception. Be specific, let the rest fly* Don't catch something specific and throw something new and vague. Worst case, rethrow with "throw"* Methods that take parameters like string message usually could be taking something with a lot more information and context* Catching an exception and not rethrowing will cause you to lose the real stack trace. Again, bad idea to toss good info.* Don't call ToUpper unless you want a culture-specific uppercase implementation. Use ToUpperInvariant. See the Turkish "i" problem.* Avoid creating half-done loggers, use NLog or Log4Net. Make sure your application takes advantage of the great and trusted open source libraries out there like these loggers and/or ELMAH. If you have to you can send your exceptions *to* ELMAH: ErrorSignal.FromCurrentContext().Raise(e);* Remember that Application_Error exists. Catch exceptions as high as you can, not as low.
base.TryExecute(() => { var repo = MyRepository.Get(something); repo.SaveChanges(); // could throw an exception});
bool success = DoSomeThings(myArg);if(!success) HandleException("Do some things failed for myArg=" + myArg");
Scott Hanselman's Productivity Tips Video
Scott at DevReach in Bulgaria in October
Developer Stand up Comedy - Coding 4 Fun
TechDays/DevDays Netherlands and Belgium:
Posts by Category Posts by Month
Greatest Hits Dev Tools List