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)); throw new ArgumentNullException(nameof(exception));
} }
var info = ExceptionParser.Parse(exception.ToString()); var info = ExceptionParser.Parse(exception);
if (info == null)
{
return new Text(exception.ToString());
}
return GetException(info, settings); return GetException(info, settings);
} }

View File

@ -1,90 +1,27 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace Spectre.Console namespace Spectre.Console
{ {
internal static class ExceptionParser 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 _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*)$"); 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) if (exception is null)
{ {
throw new ArgumentNullException(nameof(exception)); throw new ArgumentNullException(nameof(exception));
} }
var lines = exception.SplitLines(); var exceptionType = exception.GetType();
return Parse(new Queue<string>(lines)); 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 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);
} }
private static StackFrameInfo? ParseStackFrame(string frame) private static StackFrameInfo? ParseStackFrame(string frame)