如何在没有任何异常的情况下,在.NET中打印当前堆栈跟踪?

432

我有一段常规的C#代码。没有任何异常。为了调试目的,我想要以编程方式记录当前堆栈跟踪。示例:

public void executeMethod() 
{
    logStackTrace();
    method();
}
7个回答

498

看一下System.Diagnostics命名空间,里面有很多好东西!

System.Diagnostics.StackTrace t = new System.Diagnostics.StackTrace();

这真的很好,可以深入了解底层发生的事情。

我建议您研究一下日志记录解决方案(例如NLog、log4net或Microsoft模式和实践企业库),这些解决方案可能实现您的目的,甚至更多。


94
记住StackTrace非常缓慢,因此要谨慎使用。 - Jonathan Dickinson
4
我认为你的答案应该提到可以使用t.ToString()来获取堆栈跟踪的字符串表示形式,这可能是OP想要的。 - Sasino
6
如果有人想要文件名和文件行号,你至少需要调用new System.Diagnostics.StackTrace(true) - David Raab

266

使用System.Environment.StackTrace代替System.Diagnostics.StackTrace,可以返回堆栈跟踪的字符串表示形式。

另一个有用的选项是在Visual Studio中使用$CALLER$CALLSTACK调试变量,因为这可以在运行时启用而无需重新构建应用程序。


8
Environment.StackTrace 只是新建了一个 StackTrace 实例。 - Daniel
12
@Daniel: 是的,但 System.Environment.StackTrace 可能是更方便的访问该信息的方式。 - larsmoa
6
实际上,我相信你可以使用 System.Diagnostics.StackTrace 访问行号 - 请参见 http://msdn.microsoft.com/en-us/library/system.diagnostics.stackframe.getfilelinenumber.aspx - larsmoa
12
Environment.StackTrace总是以at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo) at System.Environment.get_StackTrace()开头,这难道不令人烦恼吗?因为这并不属于当前堆栈跟踪,而只是在某些人寻找它时的一个参考点。 - Rory
15
@Rory,请查看构造函数StackTrace(int skipFrames, bool fNeedFileInfo),你可以跳过开始的帧。在ILSpy中打开该方法,您就可以看到它的作用。 - Walter Verhoeven
显示剩余2条评论

39

有两种方法可以实现这一点。 System.Diagnostics.StackTrace() 将为您提供当前线程的堆栈跟踪。如果您有对 Thread 实例的引用,则可以通过重载版本的 StackTrace() 获取该线程的堆栈跟踪。

您还可以查看Stack Overflow问题 “如何获取非当前线程的堆栈跟踪?”


错误 CS1955:不可调用的成员“StackTrace”不能像方法一样使用。 - David

18

你也可以在不修改代码的情况下,在Visual Studio调试器中完成此操作。

  1. 在您想查看堆栈跟踪的位置创建断点。
  2. 右键单击断点,选择“Actions...”(在VS2015中)。 在VS2010中,选择“当命中时...”,然后启用“打印消息”。
  3. 确保选择“继续执行”。
  4. 输入您想要打印出来的一些文本。
  5. 在您希望查看堆栈跟踪的任何位置添加$CALLSTACK。
  6. 在调试器中运行程序。

当然,如果您在不同的计算机上运行代码,则这并没有帮助,但能够自动输出堆栈跟踪而无需影响发布代码甚至无需重新启动程序可能非常方便。


8
如果您想要查看堆栈跟踪,并且已经在Visual Studio调试器中断点处,请转到“调试(Debug)”菜单->“窗口”(Windows)->“调用堆栈”(Call Stack)。 - khargoosh
6
是的,但如果你这样做,程序就不需要停下来让你看堆栈跟踪。 - Hank Schultz

17
Console.WriteLine(
    new System.Diagnostics.StackTrace().ToString()
    );
输出结果将类似于:

在YourNamespace.Program.executeMethod(String msg)处

在YourNamespace.Program.Main(String[] args)处

请用您的Log方法替换Console.WriteLine方法。 实际上,对于Console.WriteLine情况,不需要.ToString(),因为它接受object。 但是您可能需要为您的Log(string msg)方法使用它。

5
这对我管用:

这个方法对我有用:

Debug.WriteLine(Environment.StackTrace);

-7
   private void ExceptionTest()
    {
        try
        {
            int j = 0;
            int i = 5;
            i = 1 / j;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error: " + ex.Message);
            var stList = ex.StackTrace.ToString().Split('\\');
            Console.WriteLine("Exception occurred at " + stList[stList.Count() - 1]);
        }
    }

对我来说似乎可以工作


8
为什么要引发一个明显的异常?为什么不直接实例化一个异常,而是实例化一个会自行引发异常的缺陷?额外的捕获逻辑有什么好处,才能得到真正的异常信息?即使这样做了,你仍然忽略了如何在没有任何异常的情况下获取堆栈跟踪(请阅读标题)的实际发布问题。 - Flater
这是一个有效的答案,但是非常糟糕。无论何时何地,都不应该使用它... @Jeff,无论你在哪里,都请用其他答案建议的像样的代码替换它。 - Tomer W

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接