Fix parsing of exceptions on .NET Framework

On .NET Framework, `exception.ToString()` uses a slightly different format than on .NET Core.

So in order to properly transform an `Exception` into an `ExceptionInfo` on both .NET Core and .NET Framework we use `exception.StackTrace` + `exception.InnerException`. As an added benefit, it greatly simplifies the implementation of the `ExceptionParser` class.
This commit is contained in:
Cédric Luthi 2021-08-20 14:37:50 +02:00 committed by Patrik Svensson
parent bf95564ebb
commit e081593012
2 changed files with 7 additions and 74 deletions

View File

@ -14,11 +14,7 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(exception));
}
var info = ExceptionParser.Parse(exception.ToString());
if (info == null)
{
return new Text(exception.ToString());
}
var info = ExceptionParser.Parse(exception);
return GetException(info, settings);
}

View File

@ -1,90 +1,27 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
namespace Spectre.Console
{
internal static class ExceptionParser
{
private static readonly Regex _messageRegex = new Regex(@"^(?'type'.*):\s(?'message'.*)$");
private static readonly Regex _stackFrameRegex = new Regex(@"^\s*\w*\s(?'method'.*)\((?'params'.*)\)");
private static readonly Regex _fullStackFrameRegex = new Regex(@"^\s*(?'at'\w*)\s(?'method'.*)\((?'params'.*)\)\s(?'in'\w*)\s(?'path'.*)\:(?'line'\w*)\s(?'linenumber'\d*)$");
public static ExceptionInfo? Parse(string exception)
public static ExceptionInfo Parse(Exception exception)
{
if (exception is null)
{
throw new ArgumentNullException(nameof(exception));
}
var lines = exception.SplitLines();
return Parse(new Queue<string>(lines));
}
private static ExceptionInfo? Parse(Queue<string> lines)
{
if (lines.Count == 0)
{
// Error: No lines to parse
return null;
}
var line = lines.Dequeue();
line = line.ReplaceExact(" ---> ", string.Empty);
var match = _messageRegex.Match(line);
if (!match.Success)
{
return null;
}
var inner = (ExceptionInfo?)null;
// Stack frames
var frames = new List<StackFrameInfo>();
while (lines.Count > 0)
{
if (lines.Peek().TrimStart().StartsWith("---> ", StringComparison.OrdinalIgnoreCase))
{
inner = Parse(lines);
if (inner == null)
{
// Error: Could not parse inner exception
return null;
}
continue;
}
line = lines.Dequeue();
if (string.IsNullOrWhiteSpace(line))
{
// Empty line
continue;
}
if (line.TrimStart().StartsWith("--- ", StringComparison.OrdinalIgnoreCase))
{
// End of inner exception
break;
}
var stackFrame = ParseStackFrame(line);
if (stackFrame == null)
{
// Error: Could not parse stack frame
return null;
}
frames.Add(stackFrame);
}
return new ExceptionInfo(
match.Groups["type"].Value,
match.Groups["message"].Value,
frames, inner);
var exceptionType = exception.GetType();
var frames = exception.StackTrace?.SplitLines().Select(ParseStackFrame).Where(e => e != null).Cast<StackFrameInfo>().ToList() ?? new List<StackFrameInfo>();
var inner = exception.InnerException is null ? null : Parse(exception.InnerException);
return new ExceptionInfo(exceptionType.FullName ?? exceptionType.Name, exception.Message, frames, inner);
}
private static StackFrameInfo? ParseStackFrame(string frame)