10

I have been writing .NET applications and have been impressed with the error handling included in the framework.

When catching an error that has been throw by the processes or somewhere in the code I like to include the message (ex.Message, which is usually pretty general) but also the stacktrace (ex.stacktrace) which helps to trace the problem back to a specific spot.

For a simple example let's say for instance that we are recording numbers to a log in a method:

public void ExampleMethod(int number){
    try{
        int num = number
        ...open connection to file
        ...write number to file
    }
    catch(Exception ex){
        .... deal with exception (ex.message,ex.stacktrace etc...)
    }
    finally{
    ...close file connection
    }
}

Is there any way to see the method called (in this case ExampleMethod) with the specific number that was passed that potentially crashed the method call? I believe you could log this perhaps in the catch block but I am interested essentially in catching the method call and parameters that caused the system to throw the exception.

Any ideas?

Kev
  • 118,037
  • 53
  • 300
  • 385
JBone
  • 3,163
  • 11
  • 36
  • 47
  • 1
    The last method invoked that caused the exception is already part of the stack trace... am I missing something with your question? – Tejs Aug 18 '11 at 15:05
  • 1
    @tejs - he wants the value of the parameters passed to the method as well. – Kev Aug 18 '11 at 15:13
  • 1
    See here: http://stackoverflow.com/questions/135782/generic-logging-of-function-parameters-in-exception-handling/136041#136041 – SwDevMan81 Aug 18 '11 at 15:47

7 Answers7

13

I suggest stuffing the parameter values into the exception's Data dictionary, e.g.

public void ExampleMethod(int number) {
try {
    int num = number
    ...open connection to file
    ...write number to file
}
catch(Exception ex) {
    ex.Data["number"] = number;
    //.... deal with exception (ex.message,ex.stacktrace etc...)
}
finally {
    //...close file connection
}

Another advantage of this method is that you can stuff the parameters in the catch block, then re-throw the exception and log it somewhere else without losing the stack trace, e.g.

catch(Exception ex) {
    ex.Data["number"] = number;
    throw;
}
Christian Hayter
  • 30,581
  • 6
  • 72
  • 99
  • 2
    I like this approach. I've always found surprising that the Data property is so rarely used... – Thomas Levesque Aug 18 '11 at 15:28
  • Wow I never heard of this feature – JBone Aug 18 '11 at 16:13
  • 2
    One caveat, Christian. "Without losing the stack trace" isn't 100% accurate. When you rethrow the exception, even with `throw;`, the stacktrace position *within that code block* gets reset. So if you're distributing PDB files to get line numbers in your stack trace, some of the line numbers will be off if you rethrow. – Toby Aug 18 '11 at 18:38
7

If you want to know the value of the parameters in your method, then there is only one way, IMO, to do it - you need to repackage the exception with data.

For example:

 int param1 = 10;
 string param2 = "Hello World";

 try
 {
     SomeMethod(param1, param2)
 }
 catch(SomeExpectedException e)
 {
      throw new MyParameterSensitiveException(e, param1, param2);
 }

You basically repackage the original exception as the inner exception of another exception, and additionally supply the parameters you used to call the method. Then you could inspect that in some way to figure out what went wrong.

Tejs
  • 40,736
  • 10
  • 68
  • 86
  • The [Exception.Data](http://msdn.microsoft.com/en-us/library/system.exception.data.aspx) property provides the ability to store the exception details. I do not see the need for a custom exception when the system exceptions provide the necessary implementation. The call to the logger (`MyLogger.Log(exceptionInstance)`) is sufficient. – Devendra D. Chavan Aug 18 '11 at 15:50
1

In order to do this:

public void MyProblematicMethod(int id, string name)
{
    try
    {
        object o = null;
        int hash = o.GetHashCode(); // throws NullReferenceException
    }
    catch (Exception ex)
    {
        string errorMessage = SummarizeMethodCall(MethodBase.GetCurrentMethod(), id, name);
        // TODO: do something with errorMessage
    }
}

...and get this:

"MyProblematicMethod invoked: id = 1, name = Charlie"

...you could do something like this:

public static string SummarizeMethodCall(MethodBase method, params object[] values)
{
    var output = new StringBuilder(method.Name + " invoked: ");
    ParameterInfo[] parameters = method.GetParameters();
    for (int i = 0; i < parameters.Length; i++)
    {
        output.AppendFormat("{0} = {1}",
            parameters[i].Name,
            i >= values.Length ? "<empty>" : values[i]
        );
        if (i < parameters.Length - 1)
            output.Append(", ");
    }
    return output.ToString();
}
D. A. Terre
  • 6,092
  • 1
  • 18
  • 18
1

The accepted answer and many of the solutions described will work fine but what you're doing is littering your source with a slightly different blob of code depending on what parameters are in your method signature.

When it comes time to add a new parameter you need to remember to update your handler to add that new parameter. Or if you remove a parameter then you need to remember to remove the parameter from your exception handler.

What if you have a two or more try..catch blocks? Then you now have two blocks of code to keep up to date. Definitely not refactor friendly.

Another approach is to remove the logging code use a technique called Aspect Oriented Programming.

One such tool to facilitate this is a product called PostSharp.

With PostSharp you can write a logger than is invoked whenever an exception is thrown without the need for messy method and parameter specific code. For example (using version 1.5 of PostSharp):

LoggerAttribute.cs -

[Serializable]
public class LoggerAttribute : OnExceptionAspect
{
  public override void OnException(MethodExecutionEventArgs eventArgs)
  {
    Console.WriteLine(eventArgs.Method.DeclaringType.Name);
    Console.WriteLine(eventArgs.Method.Name);
    Console.WriteLine(eventArgs.Exception.StackTrace);

    ParameterInfo[] parameterInfos = eventArgs.Method.GetParameters();
    object[] paramValues = eventArgs.GetReadOnlyArgumentArray();

    for (int i = 0; i < parameterInfos.Length; i++)
    {
      Console.WriteLine(parameterInfos[i].Name + "=" + paramValues[i]);
    }

    eventArgs.FlowBehavior = FlowBehavior.Default;
  }
}

You then decorate your classes with the LoggerAttribute:

[Logger]
public class MyClass
{
  public void MyMethod(int x, string name)
  {
      // Something that throws an exception
  }
}

Anything that throws an exception in MyMethod will cause the OnException method to be executed.

There are two versions of PostSharp. Version 1.5 is free and open sourced under the GPL and is targeted at .NET 2.0. PostSharp 2.0 is not entirely free but its community edition will support the basic functionality described above.

Kev
  • 118,037
  • 53
  • 300
  • 385
0

You could make a class that inherits Exception and add some arguments to it so you could pass the number to it.

Caimen
  • 2,623
  • 2
  • 26
  • 43
0

You can get the method name and the parameters like this,

try
{
    int a = 0;
    int i = 1 / a;
}
catch (Exception exception)
{
    StackTrace s = new StackTrace(exception);
    StackFrame stackFrame = s.GetFrame(s.FrameCount - 1);
    if (stackFrame != null)
    {
        StringBuilder stackBuilder = new StringBuilder();
        MethodBase method = stackFrame.GetMethod();
        stackBuilder.AppendFormat("Method Name = {0}{1}Parameters:{1}", method.Name, Environment.NewLine);

        foreach (ParameterInfo parameter in method.GetParameters())
        {
            stackBuilder.AppendFormat("{0} {1}", parameter.ParameterType.FullName, parameter.Name);
            stackBuilder.AppendLine();
        }

        // or use this to get the value
        //stackBuilder.AppendLine("param1  = " + param1);
        //stackBuilder.AppendLine("param2  = " + param2);
    }
}

I am not sure whether you can get the parameter values directly off the stack like a debugger.

Devendra D. Chavan
  • 8,871
  • 4
  • 31
  • 35
0

The Automatic Exception Handling from Crypto Obfuscator can do what you need.

The exception reports include all pertinent information including full stack trace info along with the values of all method arguments and local variables, plus the system information, the time of the exception, the build number, and optional developer defined custom data like log files, screenshots, etc.

DISCLAIMER: I work for LogicNP Software, the developer of Crypto Obfuscator.

logicnp
  • 5,796
  • 1
  • 28
  • 32