Skip to content

Instantly share code, notes, and snippets.

@houseofcat
Last active December 6, 2021 04:03
Show Gist options
  • Save houseofcat/2bf0c43c8382f56a0c5779fec13332d7 to your computer and use it in GitHub Desktop.
Save houseofcat/2bf0c43c8382f56a0c5779fec13332d7 to your computer and use it in GitHub Desktop.
C# Prettify Stack Trace
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;
}
}
}
@bboyle1234
Copy link

I modified it for my own use:

    [Serializable]
    public class ExceptionPretty {
        public string Message;
        public string ExceptionType;

        public string Method;
        public string FileName;
        public int Line;
        public List<string> StackLines = new List<string>(); // empty array [] makes better json than null
        public List<ExceptionPretty> InnerExceptions = new List<ExceptionPretty>(); // empty array [] makes better json than null

        public ExceptionPretty(Exception x) {

            Message = x.Message;
            ExceptionType = x.GetType().ToString();

            if (x is AggregateException aggregateException) {
                InnerExceptions = aggregateException.InnerExceptions.Select(inner => new ExceptionPretty(inner)).ToList();
            } else if (null != x.InnerException) {
                InnerExceptions.Add(new ExceptionPretty(x.InnerException));
            }

            var lines = (x.StackTrace ?? "") // if stack trace is null we end up with an empty array
                .Split('\n') // independent of operating system
                .Select(l => l.Replace("\r", "")) // independent of operating system
                .Where(l => !string.IsNullOrWhiteSpace(l)) // exclude empty strings
                .ToArray();

            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 };
                        Method = methodStrings[methodStrings.Length - 1];
                        FileName = fileStrings[0].Contains(".cs") ? fileStrings[0] : "System/NET Exception";
                        Line = int.Parse(fileStrings[1]);

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

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

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

@bboyle1234
Copy link

Here's an other little helper nugget that I like to use occasionally:

    public static string UnwindMessage(this Exception x, string separator = " => ") {
        return null == x.InnerException
            ? x.Message
            : x.Message + separator + x.InnerException.UnwindMessage(separator);
    }

@bboyle1234
Copy link

And then I refactored ExceptionPretty to ExceptionSummary ... until a better name can be conjured.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment