在.net异常中,如何获取带有参数值的stacktrace
我正在尝试在.net(c#)中添加一个未处理的异常处理程序,该异常处理程序应尽可能对'用户'有所帮助。最终用户主要是程序员,因此他们只需要提示他们操纵错误的对象。
当应用程序崩溃时,我正在开发类似于Windows XP错误报告的Windows,但它会立即就抛出的异常提供尽可能多的信息。
尽管堆栈跟踪使我(因为我有源代码)能够查明问题的根源,但用户却没有此问题,因此,他们会在没有更多信息的情况下迷失方向。不用说,我必须花费很多时间来支持该工具。
有一些系统异常(例如,Dictionary集合抛出的KeyNotFoundException)使我很烦,因为它们没有在消息中包含未找到的键。我可以用大量的try catch块填充我的代码,但是它相当激进,并且需要维护更多的代码,更不用说还有很多必须本地化的字符串。
最后的问题是:有什么方法(在运行时)获取调用堆栈跟踪中每个函数的参数值?
仅此一项就可以解决90%的支持电话。
解决方案
我不知道这样的事情,但是我自己想要。我通常在抛出异常时自己做,但是如果不是我们抛出的异常,那么我们可能会和我们其他人在一起。
我不相信有内置的机制。在检索堆栈跟踪的每一帧的同时,我们可以确定跟踪中的方法,但只为我们提供该方法的反射类型信息。没有参数信息。我认为这就是为什么某些异常(尤其是ArgumentException等)的原因。等提供一种机制来指定异常中涉及的参数的值,因为没有简单的方法来获取它。
我认为System.Diagnostics.StackFrame不提供参数信息(方法签名除外)。
我们可以通过AOP使用跟踪日志记录来解决麻烦的调用,甚至可以使用其异常拦截功能有条件地记录日志,而不必乱扔代码。看看http://www.postsharp.org/。
我不知道在调用堆栈跟踪中获取每个函数的参数值的方法,但是一种解决方案是捕获特定的异常(在示例中为" KeyNotFoundException"),然后将其重新抛出为新的Exception。 。这样一来,我们便可以关联所需的任何其他信息
例如:
Dim sKey as String = "some-key" Dim sValue as String = String.Empty Try sValue = Dictionary(sKey) Catch KeyEx As KeyNotFoundException Throw New KeyNotFoundException("Class.Function() - Couldn't find [" & sKey & "]", KeyEx) End Try
感谢我们关于本地化错误字符串的声明,但是,如果受众是"大多数是程序员",那么约定已经在某种程度上要求英语理解(对还是错,但这是另一场辩论!)
由于最终用户是开发人员,因此我们可以为他们提供一个版本,该版本可以记录传递的所有键值/参数。并提供便利,使他们可以打开/关闭日志记录。
遇到异常后,我们将感兴趣的两件事是System.Diagnostics.StackTrace和System.Diagnostics.StackFrame
这里有一个MSDN示例
不幸的是,除了与应用程序实际连接的调试工具之外,我们无法从调用堆栈中获取参数的实际值。但是,通过在System.Diagnostics中使用StackTrace和StackFrame对象,可以遍历调用堆栈并读出所有调用的方法以及参数名称和类型。我们将执行以下操作:
System.Diagnostics.StackTrace callStack = new System.Diagnostics.StackTrace(); System.Diagnostics.StackFrame frame = null; System.Reflection.MethodBase calledMethod = null; System.Reflection.ParameterInfo [] passedParams = null; for (int x = 0; x < callStack.FrameCount; x++) { frame = callStack.GetFrame(x); calledMethod = frame.GetMethod(); passedParams = calledMethod.GetParameters(); foreach (System.Reflection.ParameterInfo param in passedParams) System.Console.WriteLine(param.ToString()); }
如果我们需要实际值,那么我们恐怕需要进行小型转储并对其进行分析。有关获取转储信息的信息,可以在以下位置找到:
http://www.debuginfo.com/tools/clrdump.html
从理论上讲,可以利用可移植可执行(PE)文件格式来获取变量类型和偏移量,从而完成我们想做的事情,但是几年前,我遇到了[documentation]墙,试图做到这一点。祝你好运!
如果我们可以做我们想找的事情,那将是.NET安全不可或者缺的一部分。
在这种情况下,最好的选择是将其添加到调试器或者分析器(它们中的任何一个都可以访问这些值)。前者可以添加,后者需要在程序启动前处于活动状态。
同样,我在运行时没有发现任何可以自动派生参数的内容。相反,我使用了Visual Studio加载项来生成代码,这些代码显式地打包了参数,如下所示:
public class ExceptionHandler { public static bool HandleException(Exception ex, IList<Param> parameters) { /* * Log the exception * * Return true to rethrow the original exception, * else false */ } } public class Param { public string Name { get; set; } public object Value { get; set; } } public class MyClass { public void RenderSomeText(int lineNumber, string text, RenderingContext context) { try { /* * Do some work */ throw new ApplicationException("Something bad happened"); } catch (Exception ex) { if (ExceptionHandler.HandleException( ex, new List<Param> { new Param { Name = "lineNumber", Value=lineNumber }, new Param { Name = "text", Value=text }, new Param { Name = "context", Value=context} })) { throw; } } } }
编辑:或者通过使HandleException的参数成为params数组:
public static bool HandleException(Exception ex, params Param[] parameters) { ... } ... if (ExceptionHandler.HandleException( ex, new Param { Name = "lineNumber", Value=lineNumber }, new Param { Name = "text", Value=text }, new Param { Name = "context", Value=context} )) { throw; } ...
生成额外的代码以将参数显式传递给异常处理程序有点麻烦,但是使用外接程序至少可以使它自动化。
自定义属性可用于注释我们不希望外接程序传递给异常处理程序的任何参数:
public UserToken RegisterUser( string userId, [NoLog] string password ) { }
第二编辑:
提醒我们,我完全忘记了AVICode:
http://www.avicode.com/
他们使用呼叫拦截技术来提供准确的此类信息,因此它必须是可能的。