我有一个使用返回值处理错误的组件,而不是标准的异常处理。除了错误代码之外,它还返回错误发生位置的堆栈跟踪。我用来调用组件的包装器将解释返回代码并抛出异常。
我想让包装器抛出一个异常,其中包含从组件中捕获的堆栈跟踪信息。我希望它看起来好像异常是从错误的原始站点抛出的,即使它被抛到其他地方。 更具体地说,id就像visual studio测试运行器显示的堆栈跟踪,以反映正确的位置
有没有办法做到这一点?如果我可以避免访问私人成员的低级反射技巧,那也很好,但我会采取我能得到的。
编辑1: 我不关心如何捕获堆栈跟踪,我关心的是将已经捕获的堆栈跟踪附加到异常
编辑2:
我尝试重写StackTrace属性,但是visual studio正在从其他地方提取堆栈跟踪数据,并且似乎完全忽略了被覆盖的属性
CustomException GenerateExcpetion()
{
return new CustomException();
}
void ThrowException(Exception ex)
{
Trace.WriteLine("Displaying Exception");
Trace.WriteLine(ex.ToString());
var edi = ExceptionDispatchInfo.Capture(ex);
edi.Throw();
}
[TestMethod]
public void Test006()
{
var ex = GenerateExcpetion();
ThrowException(ex);
}
public class CustomException : Exception
{
string _stackTrace;
public CustomException()
{
_stackTrace = Environment.StackTrace;
}
public override string StackTrace
{
get
{
return base.StackTrace;
}
}
}
Excpetion.ToString()方法从私有属性中提取堆栈跟踪数据,因此来自覆盖的堆栈跟踪不会显示。
CustomException:抛出了类型'CustomException'的异常。
ExceptionDispatchInfo也从私有属性中查找堆栈跟踪数据,因此它无法找到任何该数据,并且当您抛出此自定义异常时,新的堆栈跟踪将附加到异常,其位置为它被扔掉了。如果直接使用throw,则私有堆栈信息将设置为抛出的位置。
答案 0 :(得分:26)
只需创建自己的Exception
类型并覆盖StackTrace
属性:
class MyException : Exception
{
private string oldStackTrace;
public MyException(string message, string stackTrace) : base(message)
{
this.oldStackTrace = stackTrace;
}
public override string StackTrace
{
get
{
return this.oldStackTrace;
}
}
}
答案 1 :(得分:7)
没有优雅可用,这是我基于反思的方法。
public static class ExceptionUtilities
{
private static readonly FieldInfo STACK_TRACE_STRING_FI = typeof(Exception).GetField("_stackTraceString", BindingFlags.NonPublic | BindingFlags.Instance);
private static readonly Type TRACE_FORMAT_TI = Type.GetType("System.Diagnostics.StackTrace").GetNestedType("TraceFormat", BindingFlags.NonPublic);
private static readonly MethodInfo TRACE_TO_STRING_MI = typeof(StackTrace).GetMethod("ToString", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { TRACE_FORMAT_TI }, null);
public static Exception SetStackTrace(this Exception target, StackTrace stack)
{
var getStackTraceString = TRACE_TO_STRING_MI.Invoke(stack, new object[] { Enum.GetValues(TRACE_FORMAT_TI).GetValue(0) });
STACK_TRACE_STRING_FI.SetValue(target, getStackTraceString);
return target;
}
}
将格式化的StackTrace字符串写入_stackTraceString属性似乎足以欺骗visual studio test runner和Exception.ToString()方法,使其相信堆栈是由throw生成的(实际上没有抛出任何东西)。
见下文用法:
StackTrace GetDeeperStackTrace(int depth)
{
if (depth > 0)
{
return GetDeeperStackTrace(depth - 1);
}
else
{
return new StackTrace(0, true);
}
}
[TestMethod]
public void Test007()
{
Exception needStackTrace = new Exception("Some exception");
var st = GetDeeperStackTrace(3);
needStackTrace.SetStackTrace(st);
Trace.Write(needStackTrace.ToString());
throw new Exception("Nested has custom stack trace", needStackTrace);
}
答案 2 :(得分:6)
您可以使用: Environment.StackTrace 在组件中出现错误时捕获堆栈跟踪,然后将其与其他错误信息或重新抛出一起返回。
您可以手动构建堆栈框架以创建完整跟踪:StackFrame
MSDN:StackTrace
答案 3 :(得分:3)
还有一个更复杂的解决方案,避免了反射部件的任何运行时开销:
public static class ExceptionExtensions
{
public static Exception SetStackTrace(this Exception target, StackTrace stack) => _SetStackTrace(target, stack);
private static readonly Func<Exception, StackTrace, Exception> _SetStackTrace = new Func<Func<Exception, StackTrace, Exception>>(() =>
{
ParameterExpression target = Expression.Parameter(typeof(Exception));
ParameterExpression stack = Expression.Parameter(typeof(StackTrace));
Type traceFormatType = typeof(StackTrace).GetNestedType("TraceFormat", BindingFlags.NonPublic);
MethodInfo toString = typeof(StackTrace).GetMethod("ToString", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { traceFormatType }, null);
object normalTraceFormat = Enum.GetValues(traceFormatType).GetValue(0);
MethodCallExpression stackTraceString = Expression.Call(stack, toString, Expression.Constant(normalTraceFormat, traceFormatType));
FieldInfo stackTraceStringField = typeof(Exception).GetField("_stackTraceString", BindingFlags.NonPublic | BindingFlags.Instance);
BinaryExpression assign = Expression.Assign(Expression.Field(target, stackTraceStringField), stackTraceString);
return Expression.Lambda<Func<Exception, StackTrace, Exception>>(Expression.Block(assign, target), target, stack).Compile();
})();
}
答案 4 :(得分:1)
对我来说,遵循以下方法似乎是最好的方法。由于.ToString()
的{{1}}使用私有字段进行堆栈跟踪,因此该方法对记录器有效。
Exception
答案 5 :(得分:-1)
var error = new Exception(ex.Message);
var STACK_TRACE_STRING_FI = typeof(Exception).GetField("_stackTraceString", BindingFlags.NonPublic | BindingFlags.Instance);
STACK_TRACE_STRING_FI.SetValue(error, ex.StackTrace);