tags:

views:

79

answers:

1

As I was working on a project, I thought to myself "Hmmm, it would be really handy to log a message, and then throw an Exception with that same message". Since this would let me keep my "exceptions are for exceptional circumstances" principle, but still make sure we're recording detailed information about what went wrong in the system.

So that gave rise to:

public static class LogAndThrow
{
    public static void Message<TException>(string message) where TException : Exception
    {
        // Log message here

        var constructor = 
            typeof(TException).GetConstructor(new[] { typeof(string) });

        throw (TException)constructor.Invoke(new[] { message });
    }
}

Sure it's a little rough (and I cut it down for this post), but it works.

However being the type of person to build Exceptions via reflection, I was annoyed that the stack trace would be "tainted" with the LogAndThrow.Message() line.

So I set off to fix that :-)

I was able to replace the stack trace with some serialization and other trickery, all very stupid, and brute force. But I wanted to figure this out just because.

But I noticed something curious:

var exception = new Exception();
throw exception;

After that exception is created, but before it is thrown, the only thing set is the Message. Stack trace, etc are empty.

The above is equivalent to the following IL:

.locals init (
    [0] class [mscorlib]System.Exception exception)
nop 
newobj instance void [mscorlib]System.Exception::.ctor()
stloc.0 
ldloc.0 
throw 

Which seems to me that the IL for 'throw' is doing something more than just taking that reference and walking it up the stack.

Does anyone know what the runtime is doing with the exception on the stack when the IL 'throw' is reached?

The trick we used below to change the stack relates to that "magic" in throw I think:

This code is horrible and wrong. More of a science experiment than anything that should ever be put in production evereververEVER

var e = new Exception("message here");
try
{
     throw e;
}
finally
{
    // Get the private file _stackTraceString with reflection
    field.SetValue(e, new StackTrace(1).ToString());
}
+4  A: 

Why can't you modify your static method to return an exception object and throw later. For example

// Do something
...
// Found error condition, need to throw an exception
if (error condition)
{
  throw LogAndThrow.Message("Message goes here");
}

Edit: AFAIK, there no way to modify stack trace. There are ways to preserve original stack trace while re throwing exceptions - see this article for it.

VinayC
It's true that would work. What I wanted was something more declarative. LogAndThrow.ErrorIf(condition, "blah");What I do going forward is probably going to resemble your suggestion.But I'm still curious what the VM is doing down at that level :-)
ecoffey