runtime: Allow setting the stack trace on deserialized exception object

Proposal

Provide a setter property for SourceExceptionStackTrace in ExceptionDispatchInfo class


namespace System.Runtime.ExceptionServices
{
    public sealed class ExceptionDispatchInfo
    {
        public string SourceExceptionStackTrace { get; set; }
    }
}

Details

Exception serialization scenario is challenging as there is no easy way can deserialize an exception object and include the stack trace in the de-serialized object. The reason is Exception lack supporting a StackTrace setter property or providing a constructor that can take stack trace as a paramter. The net core supports binary serialization which can serialize and de-serialize the exception objects including the stack trace but this approach is not helping much for the following reasons:

  • Net core is not promising supporting binary serialization across different net core versions (or with other targets like full framework).
  • Other serialization engines (e.g. DateContractSerialization) will not benefit from the binary serialization and need to have a way to fully deserialize exception objects.
  • When binary deserialize exception object which include a stack trace and then throw this de-serialized object, the stack trace after throwing is the one reflect the place where we threw this exception but the original de-serialized stack trace will be lost. The original stack trace will be preserved only in case re-throw previously thrown exception. The following code demonstrate this issue
            try
            {
                throw new Exception();
            }
            catch (Exception e)
            {
                using (MemoryStream ms = new MemoryStream(100))
                {
                    BinaryFormatter formatter = new BinaryFormatter();
                    formatter.Serialize(ms, e);

                    // reset the stream
                    ms.Position = 0;

                    var excp = (Exception)formatter.Deserialize(ms);

                    Console.WriteLine(excp.StackTrace); // this will show the right original serialized stack 

                    try { throw excp; } catch (Exception excp1) { Console.WriteLine(excp1.StackTrace);  } // this will show the new stack but the old de-serialized stack will be lost.

                    throw; // the thrown exception will include the original exception stack in “e” object 
                }
            }

To support the scenario, we need to add SourceExceptionStackTrace property to ExceptionDispatchInfo class. The following code demonstrate how ExceptionDispatchInfo can be used to get the exception with the original serialized stack trace and also how to throw the exception with the new stack trace.

            ExceptionDispatchInfo edi = ExceptionDispatchInfo.Capture(deserializedException);
            edi.SourceExceptionStackTrace = originalStackTrace;
            Exception deserializedExceptionWithOriginalStackTrace = edi.SourceException;
            edi.Throw();

Behavior ExceptionDispatchInfo.SourceExceptionStackTrace

The setter of this property will overwrite the current source trace on the source exception. the setter will not affect any other properties in the source exception object.

The getter will return the stack trace from the source exception. this can be empty string if there is no stack trace in the source #exception

Alternative Design

We can think in adding a setter for Exception.StackTrace property or expose a method like Exception AddPreservedStack(string stackTrace))

namespace System
{
    public class Exception : ISerializable, _Exception
    {
        public virtual String StackTrace { get; set; }
    }
}

I got some feedback from different people that they don’t like this option as in the main scenarios nobody should set the stack trace on the exception objects and this option can cause confusion and people misuse it.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 27 (23 by maintainers)

Most upvoted comments

Have you considered exposing it on ExceptionDispatchInfo as a design alternative? It would make the setter to be less in your face.

@gkhanna79 Any comments about this API proposal?