Tuesday, February 8, 2011

Generic logging of function parameters in exception handling

A lot of my C# code follows this pattern:

void foo(string param1, string param2, string param3)
{
    try
    {
         // do something...
    }
    catch(Exception ex)
    {
        LogError(String.Format("Error in foo(param1={0}, param2={1}, param3={2}), exception={3}", param1, param2, param3, ex.Message));
    }
}

Is there a way in .NET to get a Key/Value list of the parameters to a function so that I can call another function to construct my error logging string? OR Do you have a more generic / better way of doing this?

  • When I have done this I just created a generic dictionary for the logging.

    I have this LogArgs class. And logging in a base class that I call when I have an exception.

    public class LogArgs
    {
    
        public string MethodName { get; set; }
        public string ClassName { get; set; }
        public Dictionary<string, object> Paramters { get; set; }
    
    
        public LogArgs()
        {
            this.Paramters = new Dictionary<string, object>();
        }
    
    }
    

    Then at the start of every method I do

    LogArgs args = new LogArgs { ClassName = "ClassName", MethodName = "MethodName" };
    args.Paramters.Add("Param1", param1);
    args.Paramters.Add("Param2", param2);
    args.Paramters.Add("Param3", param3);
    
    base.Logger.MethodStartLog(args);
    

    When I have an error I log it this way.

    base.Logger.LogError(args, ex);
    
  • No there isn't a way to do this.

    The normal practice is to not catch exceptions unless you can handle them.

    I.e. you would normally only catch exceptions and log them in a top-level exception handler. You will then get a stack trace, but won't of course get details of all the parameters of all method calls in the stack.

    Obviously when debugging you want as much detail as possible. Other ways to achieve this are:

    • Use Debug.Assert statements liberally to test assumptions you are making.

    • Instrument your application with logging that can be activate selectively. I use Log4Net, but there are also other alternatives, including using the System.Diagnostics.Trace class.

    In any case, if you do catch exceptions only to log them (I'd do this at a tier boundary in an n-tier application, so that exceptions are logged on the server), then you should always rethrow them:

    try
    {
        ...
    }
    catch(Exception ex)
    {
        log(ex);
        throw;
    }
    
    From Joe
  • You could use a similar style of constructing the message, but add the params keyword in your LogError method to handle the arguments. For example:

        public void LogError(string message, params object[] parameters)
        {
            if (parameters.Length > 0)
                LogError(string.Format(message, parameters));
            else
                LogError(message);
        }
    
    From JoshL
  • You could use Reflection and the convention that you must pass the parameters to the LogError with the right order:

    private static void MyMethod(string s, int x, int y)
    {
        try
        {
            throw new NotImplementedException();
        }
        catch (Exception ex)
        {
            LogError(MethodBase.GetCurrentMethod(), ex, s, x, y);
        }
    }
    
    private static void LogError(MethodBase method, Exception ex, params object[] values)
    {
        ParameterInfo[] parms = method.GetParameters();
        object[] namevalues = new object[2 * parms.Length];
    
        string msg = "Error in " + method.Name + "(";
        for (int i = 0, j = 0; i < parms.Length; i++, j += 2)
        {
            msg += "{" + j + "}={" + (j + 1) + "}, ";
            namevalues[j] = parms[i].Name;
            if (i < values.Length) namevalues[j + 1] = values[i];
        }
        msg += "exception=" + ex.Message + ")";
        Console.WriteLine(string.Format(msg, namevalues));
    }
    
    From Panos
  • You could use aspect oriented programming with PostSharp (have a look at http://www.postsharp.org, and the tutorial at http://www.codeproject.com/KB/cs/ps-custom-attributes-1.aspx). Basically you could do something like this:

    public class LogExceptionAttribute : OnExceptionAspect
    {
     public override void OnException(MethodExecutionEventArgs eventArgs)
     {
      log.error("Exception occurred in method {0}", eventArgs); 
     }
    }
    
    [LoggingOnExceptionAspect]
    public foo(int number, string word, Person customer)
    {
       \\ ... something here throws an exception
    }
    

    Perhaps not quite what you want, but I'm sure it can be adapted to suit your needs.

    From Jacob

0 comments:

Post a Comment