Last active
December 6, 2021 04:03
-
-
Save houseofcat/2bf0c43c8382f56a0c5779fec13332d7 to your computer and use it in GitHub Desktop.
C# Prettify Stack Trace
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
Can you update this to work for aggregate exceptions as well please?
It would be my absolute pleasure to do so.
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 ", " @ ")); }
}
}
}
}
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);
}
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
Example usage:
Sample input string:
Output serialized to JSON: