Allow demystifier to add strings to existing StringBuilder (second attempt) (#80)

* Allow demystifier to add strings to existing StringBuilder

* Add exception message
This commit is contained in:
Alexander Sher 2019-02-10 12:50:33 -08:00 committed by Ben Adams
parent a9830f38e3
commit fbd6ea49ab
7 changed files with 162 additions and 78 deletions

View File

@ -173,11 +173,10 @@ namespace System.Diagnostics
// ResolveStateMachineMethod may have set declaringType to null
if (type != null)
{
var declaringTypeName = TypeNameHelper.GetTypeDisplayName(type, fullName: true, includeGenericParameterNames: true);
methodDisplayInfo.DeclaringTypeName = declaringTypeName;
methodDisplayInfo.DeclaringType = type;
}
if (method is System.Reflection.MethodInfo mi)
if (method is MethodInfo mi)
{
var returnParameter = mi.ReturnParameter;
if (returnParameter != null)
@ -190,7 +189,6 @@ namespace System.Diagnostics
{
Prefix = "",
Name = "",
Type = TypeNameHelper.GetTypeDisplayName(mi.ReturnType, fullName: false, includeGenericParameterNames: true).ToString(),
ResolvedType = mi.ReturnType,
};
}
@ -542,7 +540,6 @@ namespace System.Diagnostics
{
var parameterType = parameter.ParameterType;
var prefix = GetPrefix(parameter, parameterType);
var parameterTypeString = "?";
if (parameterType == null)
{
@ -550,7 +547,6 @@ namespace System.Diagnostics
{
Prefix = prefix,
Name = parameter.Name,
Type = parameterTypeString,
ResolvedType = parameterType,
};
}
@ -574,40 +570,22 @@ namespace System.Diagnostics
parameterType = parameterType.GetElementType();
}
parameterTypeString = TypeNameHelper.GetTypeDisplayName(parameterType, fullName: false, includeGenericParameterNames: true);
return new ResolvedParameter
{
Prefix = prefix,
Name = parameter.Name,
Type = parameterTypeString,
ResolvedType = parameterType,
};
}
private static ResolvedParameter GetValueTupleParameter(IList<string> tupleNames, string prefix, string name, Type parameterType)
{
string typeName;
if (parameterType.IsValueTuple())
{
typeName = GetValueTupleParameterName(tupleNames, parameterType);
}
else
{
// Need to unwrap the first generic argument first.
var genericTypeName = TypeNameHelper.GetTypeNameForGenericType(parameterType);
var valueTupleFullName = GetValueTupleParameterName(tupleNames, parameterType.GetGenericArguments()[0]);
typeName = $"{genericTypeName}<{valueTupleFullName}>";
}
return new ResolvedParameter
return new ValueTupleResolvedParameter
{
TupleNames = tupleNames,
Prefix = prefix,
Name = name,
Type = typeName,
ResolvedType = parameterType,
ResolvedType = parameterType
};
}

View File

@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Collections.Generic.Enumerable;
using System.Reflection;
using System.Text;
namespace System.Diagnostics
{
@ -12,27 +13,16 @@ namespace System.Diagnostics
{
private static readonly FieldInfo stackTraceString = typeof(Exception).GetField("_stackTraceString", BindingFlags.Instance | BindingFlags.NonPublic);
public static T Demystify<T>(this T exception) where T : Exception
=> Demystify(exception, originalStacksTracker: null);
private static string GetStackTracesString(this Exception exception)
=> (string)stackTraceString.GetValue(exception);
private static void SetStackTracesString(this Exception exception, string value)
=> stackTraceString.SetValue(exception, value);
/// <summary>
/// Demystifies the given <paramref name="exception"/> and tracks the original stack traces for the whole exception tree.
/// </summary>
private static T Demystify<T>(this T exception, Dictionary<Exception, string> originalStacksTracker) where T : Exception
public static T Demystify<T>(this T exception) where T : Exception
{
try
{
if (originalStacksTracker?.ContainsKey(exception) == false)
{
originalStacksTracker[exception] = exception.GetStackTracesString();
}
var stackTrace = new EnhancedStackTrace(exception);
if (stackTrace.FrameCount > 0)
@ -44,11 +34,11 @@ namespace System.Diagnostics
{
foreach (var ex in EnumerableIList.Create(aggEx.InnerExceptions))
{
ex.Demystify(originalStacksTracker);
ex.Demystify();
}
}
exception.InnerException?.Demystify(originalStacksTracker);
exception.InnerException?.Demystify();
}
catch
{
@ -69,27 +59,6 @@ namespace System.Diagnostics
/// </remarks>
[Contracts.Pure]
public static string ToStringDemystified(this Exception exception)
{
try
{
var originalStacks = new Dictionary<Exception, string>();
exception.Demystify(originalStacks);
var result = exception.ToString();
foreach (var kvp in originalStacks)
{
kvp.Key.SetStackTracesString(kvp.Value);
}
return result;
}
catch
{
// Processing exceptions shouldn't throw exceptions; if it fails
}
return exception.ToString();
}
=> new StringBuilder().AppendDemystified(exception).ToString();
}
}

View File

@ -11,7 +11,7 @@ namespace System.Diagnostics
{
public MethodBase MethodBase { get; set; }
public string DeclaringTypeName { get; set; }
public Type DeclaringType { get; set; }
public bool IsAsync { get; set; }
@ -39,11 +39,9 @@ namespace System.Diagnostics
internal StringBuilder Append(StringBuilder builder)
{
if (IsAsync)
{
builder
.Append("async ");
builder.Append("async ");
}
if (ReturnParameter != null)
@ -52,7 +50,7 @@ namespace System.Diagnostics
builder.Append(" ");
}
if (!string.IsNullOrEmpty(DeclaringTypeName))
if (DeclaringType != null)
{
if (Name == ".ctor")
@ -60,17 +58,16 @@ namespace System.Diagnostics
if (string.IsNullOrEmpty(SubMethod) && !IsLambda)
builder.Append("new ");
builder.Append(DeclaringTypeName);
AppendDeclaringTypeName(builder);
}
else if (Name == ".cctor")
{
builder.Append("static ");
builder.Append(DeclaringTypeName);
AppendDeclaringTypeName(builder);
}
else
{
builder
.Append(DeclaringTypeName)
AppendDeclaringTypeName(builder)
.Append(".")
.Append(Name);
}
@ -145,5 +142,10 @@ namespace System.Diagnostics
return builder;
}
private StringBuilder AppendDeclaringTypeName(StringBuilder builder)
{
return DeclaringType != null ? builder.AppendTypeDisplayName(DeclaringType, fullName: true, includeGenericParameterNames: true) : builder;
}
}
}

View File

@ -9,8 +9,6 @@ namespace System.Diagnostics
{
public string Name { get; set; }
public string Type { get; set; }
public Type ResolvedType { get; set; }
public string Prefix { get; set; }
@ -25,7 +23,15 @@ namespace System.Diagnostics
.Append(" ");
}
sb.Append(Type);
if (ResolvedType != null)
{
AppendTypeName(sb);
}
else
{
sb.Append("?");
}
if (!string.IsNullOrEmpty(Name))
{
sb.Append(" ")
@ -34,5 +40,10 @@ namespace System.Diagnostics
return sb;
}
protected virtual void AppendTypeName(StringBuilder sb)
{
sb.AppendTypeDisplayName(ResolvedType, fullName: false, includeGenericParameterNames: true);
}
}
}

View File

@ -0,0 +1,56 @@
// Copyright (c) Ben A Adams. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic.Enumerable;
using System.Text;
namespace System.Diagnostics
{
public static class StringBuilderExtentions
{
public static StringBuilder AppendDemystified(this StringBuilder builder, Exception exception)
{
try
{
var stackTrace = new EnhancedStackTrace(exception);
builder.Append(exception.GetType());
if (!string.IsNullOrEmpty(exception.Message))
{
builder.Append(": ").Append(exception.Message);
}
builder.Append(Environment.NewLine);
if (stackTrace.FrameCount > 0)
{
stackTrace.Append(builder);
}
if (exception is AggregateException aggEx)
{
foreach (var ex in EnumerableIList.Create(aggEx.InnerExceptions))
{
builder.AppendInnerException(ex);
}
}
if (exception.InnerException != null)
{
builder.AppendInnerException(exception.InnerException);
}
}
catch
{
// Processing exceptions shouldn't throw exceptions; if it fails
}
return builder;
}
private static void AppendInnerException(this StringBuilder builder, Exception exception)
=> builder.Append(" ---> ")
.AppendDemystified(exception)
.AppendLine()
.Append(" --- End of inner exception stack trace ---");
}
}

View File

@ -7,7 +7,7 @@ using System.Text;
namespace System.Diagnostics
{
// Adapted from https://github.com/aspnet/Common/blob/dev/shared/Microsoft.Extensions.TypeNameHelper.Sources/TypeNameHelper.cs
public class TypeNameHelper
public static class TypeNameHelper
{
public static readonly Dictionary<Type, string> BuiltInTypeNames = new Dictionary<Type, string>
{
@ -43,6 +43,12 @@ namespace System.Diagnostics
return builder.ToString();
}
public static StringBuilder AppendTypeDisplayName(this StringBuilder builder, Type type, bool fullName = true, bool includeGenericParameterNames = false)
{
ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames));
return builder;
}
/// <summary>
/// Returns a name of given generic type without '`'.
/// </summary>

View File

@ -0,0 +1,62 @@
// Copyright (c) Ben A Adams. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.Internal;
using System.Text;
namespace System.Diagnostics
{
public class ValueTupleResolvedParameter : ResolvedParameter
{
public IList<string> TupleNames { get; set; }
protected override void AppendTypeName(StringBuilder sb)
{
if (ResolvedType.IsValueTuple())
{
AppendValueTupleParameterName(sb, ResolvedType);
}
else
{
// Need to unwrap the first generic argument first.
sb.Append(TypeNameHelper.GetTypeNameForGenericType(ResolvedType));
sb.Append("<");
AppendValueTupleParameterName(sb, ResolvedType.GetGenericArguments()[0]);
sb.Append(">");
}
}
private void AppendValueTupleParameterName(StringBuilder sb, Type parameterType)
{
sb.Append("(");
var args = parameterType.GetGenericArguments();
for (var i = 0; i < args.Length; i++)
{
if (i > 0)
{
sb.Append(", ");
}
sb.AppendTypeDisplayName(args[i], fullName: false, includeGenericParameterNames: true);
if (i >= TupleNames.Count)
{
continue;
}
var argName = TupleNames[i];
if (argName == null)
{
continue;
}
sb.Append(" ");
sb.Append(argName);
}
sb.Append(")");
}
}
}