Subclassing Exception to log some contextual information

Today I arrived at work to find some truly wonderful emails in my inbox, containing the informative error:

String or binary data would be truncated.

So somewhere in a public method, one of the members of a structure in scope was too big for the database field we tried to save it to, and something somewhere was unable to save. Easy to fix if I know what method and what field it was, but I know nothing of the sort. Now what?

The error handlers in this application simply email the error message to everyone in the team. I needed a quick and dirty way to extend them such that they logged whatever relevant contextual information was available in the scope of the method that broke.

I decided to subclass Exception, adding a collection of objects to my custom exception, then have a LogError method that returns a string, on my derived class, so that I can simply call it in the methods that handle exceptions and send emails. It worked out quite nicely.

Unlike last time, I can share this code, because this time it is all downloaded from StackOverflow. I used the code from…

What this code does is, it logs every error message from the topmost Exception and every inner Exception, and then lists all of the instances added to the contextInfo parameter by class name, with the name and value of each public property of those instances on a new line. This was more than enough to tell me exactly what the issue was.

Here is the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SampleWcfService
{
    internal static class ExceptionExtensions
    {
        public static IEnumerable<TSource> FromHierarchy<TSource>(this TSource source,
            Func<TSource, TSource> nextItem, Func<TSource, bool> canContinue)
        {
            for (var current = source; canContinue(current); current = nextItem(current))
            {
                yield return current;
            }
        }

        public static IEnumerable<TSource> FromHierarchy<TSource>(this TSource source,
            Func<TSource, TSource> nextItem) where TSource : class
        {
            return FromHierarchy(source, nextItem, s => s != null);
        }

        public static string GetaAllMessages(this Exception exception)
        {
            var messages = exception.FromHierarchy(ex => ex.InnerException)
                .Select(ex => ex.Message);
            return String.Join(Environment.NewLine, messages);
        }
    }

    public class SampleServiceException : Exception
    {
        private List<object> contextInfo = new List<object>();

        public SampleServiceException(string message, Exception innerException, List<object> contextInfo)
            : base(message, innerException)
        {
            this.contextInfo = contextInfo;
        }

        public string LogError()
        {
            StringBuilder builder = new StringBuilder();

            builder.AppendLine("Exception: " + this.GetaAllMessages());
            builder.AppendLine();

            if (contextInfo.Count > 0)
            {
                foreach (var obj in contextInfo)
                {
                    builder.AppendLine(obj.GetType().Name + ":");

                    foreach (var prop in obj.GetType().GetProperties())
                    {
                        builder.AppendLine(prop.Name + " = " + prop.GetValue(obj, null));
                    }
                    builder.AppendLine();
                }
            }

            return builder.ToString();
        }
    }
}

And here is an example of its usage. For brevity, I have omitted error checking and null checking. In practice it may be better to, for example, check that everything you pass to the context info collection is valid, and if you have collections whose properties you need to see, iterate them and pass the members you need…

        public Response ExampleMethod(Request request)
        {
            Response response = new Response();

            try
            {
                try
                {
                    // Do something here that populates response...
                }
                catch (Exception ex)
                {
                    List<object> contextInfo = new List<object>();

                    contextInfo.Add(request);
                    contextInfo.Add(response);

                    throw new SampleServiceException("Unexpected error in ExampleMethod.", ex, contextInfo);
                }
            }
            catch (SampleServiceException sex)
            {
                var errorBody = sex.LogError();

                // Code to notify here, using email or whatever...
            }

            return response;
        }
Advertisements

About Jerome

I am a senior C# developer in Johannesburg, South Africa. I am also a recovering addict, who spent nearly eight years using methamphetamine. I write on my recovery blog about my lessons learned and sometimes give advice to others who have made similar mistakes, often from my viewpoint as an atheist, and I also write some C# programming articles on my programming blog.
This entry was posted in Programming and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s