Async-Friendly Stack Trace
Async-friendly format for stack traces and exceptions.
Also check out Ben's Demystifier which resolves async, iterators, tuples, location functions and more.
System.Exception: Crash! Boom! Bang!
at async AsyncFriendlyStackTrace.Test.Example1.C(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 26
at async AsyncFriendlyStackTrace.Test.Example1.B(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 20
at async AsyncFriendlyStackTrace.Test.Example1.A(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 15
at async AsyncFriendlyStackTrace.Test.Example1.Run(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 10
at AsyncFriendlyStackTrace.Test.Program.Run[TExample](TextWriter writer) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Program.cs:line 45
Install
Install-Package AsyncFriendlyStackTrace
Usage
To format exceptions, use the extension methods in ExceptionExtensions
:
exception.ToAsyncString();
This produces an async-friendly format, as you can see in the examples below. There is also special handling for AggregateException
s and ReflectionTypeLoadException
(which can contain multiple inner exceptions).
The main formatting work is done by the StackTraceExtensions.ToAsyncString
extension method. The async-friendly formatting is archieved by:
- Skipping all awaiter frames (all methods in types implementing
INotifyCompletion
) andExceptionDispatchInfo
frames. - Inferring the original method name from the async state machine class (
IAsyncStateMachine
) and removing the "MoveNext" - currently only for C#. - Adding the "async" prefix after "at" on each line for async invocations.
- Appending "(?)" to the method signature to indicate that parameter information is missing.
- Removing the "End of stack trace from previous location..." text.
Example outputs
In all the examples, OLD refrers to ToString()
output, while NEW is ToAsyncString()
.
- Example 1 (code): A simple 3 async method chain.
- Example 2 (code): Async invocations with a synchronous
Wait()
in the middle, causing anAggregateException
. - Example 3 (code): Bad Serialization - When exception is serialized and deserialized, its stack trace is saved as string. So we can't reformat the stack trace. The "new" stack trace is still a bit shorter due to an improved
AggregateException
formatting (the first inner exception isn't repeated twice). - Example 4 (code): Good Serialization - We use the
PrepareForAsyncSerialization
before serializing the exception. This saves the async-friendly stack trace as a string in theData
dictionary of the exception. This has two downsides:- The serialized data will now contain both stack trace formats.
- When using the
DataContractSerializer
, you must includeexception.Data.GetType()
as a known type. This is because its concrete type (ListDictionaryInternal
) is internal.