C# - PrettifyStackTraces


This is more or less a code snippet from my gists on GitHub.

the goal here was to make exception stacktraces more readable/JSON-friendly so they could be returned to clients or log service or just store it cleaner in the database.

Example Usage

using Newtonsoft.Json;
using static PrettifyStackTrace.Helpers;

...

var stacky = PrettifyStackTrace(stackTrace, typeof(Exception));
var myJson = JsonConvert.SerializeObject(stacky);

The Code

using System;
using System.Collections.Generic;

namespace PrettifyStackTrace
{
    public class Stacky
    {
        public string ExceptionType { get; set; }
        public string Method { get; set; }
        public string FileName { get; set; }
        public int Line { get; set; }
        public List<string> StackLines { get; set; }
    }

    public static class Helpers
    {
        public static Stacky PrettifyStackTrace(string stackTrace, Type exceptionType)
        {
            var stacky = new Stacky();
            stacky.ExceptionType = exceptionType.ToString();

            if (!string.IsNullOrEmpty(stackTrace))
            {
                stacky.StackLines = new List<string>();

                var lines = stackTrace.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);

                var stackCount = 0;

                try // Try to Prettify
                {
                    for (int i = 0; i < lines.Length; i++)
                    {
                        var line = lines[i];

                        if (i == 0)
                        {
                            var subStrings = line.Split(new string[] { " in " }, StringSplitOptions.RemoveEmptyEntries);
                            var methodStrings = subStrings[0].Split(new string[] { "   at " }, StringSplitOptions.RemoveEmptyEntries);
                            var fileStrings = (subStrings.Length > 1) ? subStrings[1].Split(new string[] { ":line " }, StringSplitOptions.RemoveEmptyEntries) : new string[] { subStrings[0], string.Empty };
                            stacky.Method = methodStrings[methodStrings.Length - 1];
                            stacky.FileName = fileStrings[0].Contains(".cs") ? fileStrings[0] : "System/NET Exception";
                            stacky.Line = int.Parse(fileStrings[1]);

                            stacky.StackLines.Add(stacky.Method);
                        }
                        else if (line.StartsWith("---"))
                        {
                            stackCount++;
                            stacky.StackLines.Add($"=== Sub-stack {stackCount} ===");
                        }
                        else
                        {
                            stacky.StackLines.Add(line.Replace("   at ", " @ "));
                        }
                    }
                }
                catch // Else just print the lines as is.
                {
                    stackCount = 0;
                    stacky.StackLines.Clear();

                    for (int i = 0; i < lines.Length; i++)
                    {
                        var line = lines[i];

                        if (line.StartsWith("---"))
                        {
                            stackCount++;
                            stacky.StackLines.Add($"=== Sub-stack {stackCount} ===");
                        }
                        else
                        { stacky.StackLines.Add(line.Replace("   at ", " @ ")); }
                    }
                }
            }

            return stacky;
        }
    }
}

Input & Output

Input
"   at [REDACTED].Controllers.v1.AdminController.PostStackTrace(String stackTrace) in C:\\[REDACTED]\\Controllers\\v1\\AdminController.cs:line 125
   at lambda_method(Closure , Object , Object[] )
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass6_1.b__3(Object instance, Object[] methodParameters)
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.ApiControllerActionInvoker.d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Filters.ActionFilterAttribute.d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Web.Http.Filters.ActionFilterAttribute.d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Filters.ActionFilterAttribute.d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.ActionFilterResult.d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Filters.AuthorizationFilterAttribute.d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.ExceptionFilterResult.d__6.MoveNext()"
Output (JSONified)
{
    "ExceptionType": "System.Exception",
    "Method": "[REDACTED].Controllers.v1.AdminController.PostStackTrace(String stackTrace)",
    "FileName": "C:\\[REDACTED]\\Controllers\\v1\\AdminController.cs",
    "Line": 125,
    "StackLines": [
        "[REDACTED].Controllers.v1.AdminController.PostStackTrace(String stackTrace)",
        " @ lambda_method(Closure , Object , Object[] )",
        " @ System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass6_1.b__3(Object instance, Object[] methodParameters)",
        " @ System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)",
        "=== Sub-stack 1 ===",
        " @ System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()",
        " @ System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)",
        " @ System.Web.Http.Controllers.ApiControllerActionInvoker.d__1.MoveNext()",
        "=== Sub-stack 2 ===",
        " @ System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()",
        " @ System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)",
        " @ System.Web.Http.Filters.ActionFilterAttribute.d__6.MoveNext()",
        "=== Sub-stack 3 ===",
        " @ System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()",
        " @ System.Web.Http.Filters.ActionFilterAttribute.d__6.MoveNext()",
        "=== Sub-stack 4 ===",
        " @ System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()",
        " @ System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)",
        " @ System.Web.Http.Filters.ActionFilterAttribute.d__5.MoveNext()",
        "=== Sub-stack 5 ===",
        " @ System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()",
        " @ System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)",
        " @ System.Web.Http.Controllers.ActionFilterResult.d__5.MoveNext()",
        "=== Sub-stack 6 ===",
        " @ System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()",
        " @ System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)",
        " @ System.Web.Http.Filters.AuthorizationFilterAttribute.d__3.MoveNext()",
        "=== Sub-stack 7 ===",
        " @ System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()",
        " @ System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)",
        " @ System.Web.Http.Controllers.ExceptionFilterResult.d__6.MoveNext()\""
    ]
}