如何在 Java 中获取当前堆栈跟踪?

Posted

技术标签:

【中文标题】如何在 Java 中获取当前堆栈跟踪?【英文标题】:How can I get the current stack trace in Java? 【发布时间】:2010-11-07 07:57:24 【问题描述】:

如何在 Java 中获取当前的 stack trace,就像在 .NET 中你可以如何获取 Environment.StackTrace

我找到了Thread.dumpStack(),但这不是我想要的——我想找回堆栈跟踪,而不是打印出来。

【问题讨论】:

在 Eclipse 中调试时,我总是使用“new Exception().printStackTrace()”作为监视表达式。当您在断点处暂停并想知道您来自哪里时,这很方便。 @TimBüthe:当您处于调试模式时,Eclipse 不是已经告诉您堆栈跟踪了吗?我想是的。 Arrays.toString(Thread.currentThread().getStackTrace());很简单。 @Arkanon 它指的是 java,并展示了他们将如何在 .net 中执行此操作以及在 java 中的等价物。 要打印得很好,你可以使用 apache StringUtils: StringUtils.join(currentThread().getStackTrace(), "\n"); 【参考方案1】:

对于只想将当前堆栈跟踪记录到他们的日志的人,我会选择:

getLogger().debug("Message", new Throwable());

干杯

【讨论】:

不要忘记创建 Throwable 的新实例是一项缓慢的操作,并且会减慢您的代码速度,即使您禁用了日志上的调试级别。最好的方法是验证是否启用了调试级别“if (getLogger().isDebugEnabled()) /*log code*/。无论如何,问题是“我想取回堆栈跟踪,而不是打印出来” ...【参考方案2】:

作为对已接受答案的评论,Tony 给出了似乎是实际回答 OP 的问题的最佳答案:

Arrays.toString(Thread.currentThread().getStackTrace()).replace( ',', '\n' );

... OP 确实询问如何从Exception 的堆栈跟踪中获取String。虽然我是 Apache Commons 的忠实拥护者,但是当有像上面这样简单的事情时,没有合乎逻辑的理由使用外部库。

【讨论】:

我有一个线程列表,我需要打印它们的堆栈跟踪。 在这种情况下,请确保使用Thread.getAllStackTraces(),它会为您提供Map<Thread, StackTraceElement[]>。 Hotspot 将在需要安全点时对所有线程进行安全点。 Zing 可以将单个线程带到安全点,而不会干扰其他线程。获取堆栈跟踪需要一个安全点。 Thread.getAllStackTraces() 获取所有堆栈跟踪并仅停止所有线程一次。当然,你必须找出你真正感兴趣的映射。【参考方案3】:

您可以使用@987654321@

返回一个由StackTraceElements 组成的数组,表示程序的当前堆栈跟踪。

【讨论】:

如果您已经在使用 apache commons 来获取字符串,这是一个很酷的单行:String fullStackTrace = org.apache.commons.lang.exception.ExceptionUtils.getFullStackTrace(e);***.com/a/10620951/11236 数组中的第一个元素(索引 0)是 java.lang.Thread.getStackTrace 方法,第二个(索引 1)通常是第一个感兴趣的元素。 一种将堆栈跟踪转换为字符串的方法,当您没有异常且未使用 Apache Arrays.toString(Thread.currentThread().getStackTrace()) @Tony 的解决方案更好,因为它不需要 throwable 正如下面许多答案所指出的 - new Throwable().getStackTrace() 要快得多,因为它不需要检查 this != Thread.currentThread() 并绕过通过子类调用它的潜在 JVM 开销 (Exception extends Throwable )【参考方案4】:

获取堆栈跟踪:

StackTraceElement[] ste = Thread.currentThread().getStackTrace();

打印堆栈跟踪(JAVA 8+):

Arrays.asList(ste).forEach(System.out::println);

打印堆栈记录 (JAVA 7):

StringBuilder sb = new StringBuilder();

for (StackTraceElement st : ste) 
    sb.append(st.toString() + System.lineSeparator());

System.out.println(sb);

【讨论】:

【参考方案5】:

我使用了上面的答案并添加了格式

public final class DebugUtil 

    private static final String SEPARATOR = "\n";

    private DebugUtil() 
    

    public static String formatStackTrace(StackTraceElement[] stackTrace) 
        StringBuilder buffer = new StringBuilder();
        for (StackTraceElement element : stackTrace) 
            buffer.append(element).append(SEPARATOR);
        
        return buffer.toString();
    

    public static String formatCurrentStacktrace() 
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        return formatStackTrace(stackTrace);
    

【讨论】:

【参考方案6】:

我建议

  Thread.dumpStack()

是一种更简单的方法,并且具有在可能根本没有问题时不实际构造异常或可抛出异常的优点,而且更重要。

【讨论】:

好吧,其实dumpStack()是一个静态方法,应该是静态访问。谢谢,日食! 我喜欢这个快捷方式,但是这个方法的源代码是:new Exception("Stack trace").printStackTrace(); 所以它实际上是在构建一个 Throwable 问题是“我找到了 Thread.dumpStack() 但这不是我想要的 - 我想找回堆栈跟踪,而不是打印出来。”【参考方案7】:

在 Java 9 中有一种新方法:

public static void showTrace() 

  List<StackFrame> frames =
    StackWalker.getInstance( Option.RETAIN_CLASS_REFERENCE )
               .walk( stream  -> stream.collect( Collectors.toList() ) );

  for ( StackFrame stackFrame : frames )
    System.out.println( stackFrame );

【讨论】:

有趣。 :-)【参考方案8】:

这是一个旧帖子,但这是我的解决方案:

Thread.currentThread().dumpStack();

那里有更多信息和更多方法: http://javarevisited.blogspot.fr/2013/04/how-to-get-current-stack-trace-in-java-thread.html

【讨论】:

由于 Thread.dumpStack() 是静态的,你应该直接调用它。 OP 表示他们希望在代码中引用堆栈跟踪,而不是打印出来。 正如@DavidAvendasora 所说,您应该直接调用Thread.dumpStack(),因为它是静态方法。 问题是“我找到了 Thread.dumpStack() 但这不是我想要的 - 我想找回堆栈跟踪,而不是打印出来。”【参考方案9】:

如果要检查进程的当前调用堆栈,可以使用 jstack 实用程序。

Usage:
    jstack [-l] <pid>
        (to connect to running process)
    jstack -F [-m] [-l] <pid>
        (to connect to a hung process)
    jstack [-m] [-l] <executable> <core>
        (to connect to a core file)
    jstack [-m] [-l] [server_id@]<remote server IP or hostname>
        (to connect to a remote debug server)

Options:
    -F  to force a thread dump. Use when jstack <pid> does not respond (process is hung)
    -m  to print both java and native frames (mixed mode)
    -l  long listing. Prints additional information about locks
    -h or -help to print this help message

【讨论】:

【参考方案10】:

您可以为此使用 Apache 的公共资源:

String fullStackTrace = org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace(e);

【讨论】:

这是一个不错的单行解决方案。仅供参考,对于使用org.apache.commons.lang3 的任何人,整行是:org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace(e); 只是在移动到org.apache.commons.lang3 时重申fileoffset 的评论,我最初忽略了这一点——导入使用lang3 而不是lang,并将getFullStackTrace 替换为getStackTrace。跨度> 这很有用!使用 IDE(例如 IntelliJ IDEA)进行调试时,最好使用 ExceptionUtils.getStackTrace(new Throwable()) 表达式来获取堆栈跟踪。【参考方案11】:

另一种解决方案(仅35 31个字符):

new Exception().printStackTrace();   
new Error().printStackTrace();

【讨论】:

很棒的解决方案。现在我不必遍历所有堆栈帧【参考方案12】:

android 上一个更简单的方法是使用这个:

import android.util.Log;
String stackTrace = Log.getStackTraceString(exception); 

【讨论】:

getStackTraceString 是在哪里定义的?这是来自第三方日志库吗? android.util.Log.getStackTraceString developer.android.com/reference/android/util/Log.html【参考方案13】:
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();

数组的最后一个元素代表堆栈的底部,它是序列中最近的方法调用。

A StackTraceElement has getClassName(), getFileName(), getLineNumber() and getMethodName().

遍历 StackTraceElement 并获得您想要的结果。

for (StackTraceElement ste : stackTraceElements ) 

    //do your stuff here...

【讨论】:

感谢您提到最后一个元素包含最新的。违反直觉,为我节省了一些时间。 还有 .toString() 会将所有数据很好地输出到字符串中【参考方案14】:

也许你可以试试这个:

catch(Exception e)

    StringWriter writer = new StringWriter();
    PrintWriter pw = new PrintWriter(writer);
    e.printStackTrace(pw);
    String errorDetail = writer.toString();

字符串 'errorDetail' 包含堆栈跟踪。

【讨论】:

【参考方案15】:

用番石榴串起来:

Throwables.getStackTraceAsString(new Throwable())

【讨论】:

【参考方案16】:
for (StackTraceElement ste : Thread.currentThread().getStackTrace()) 
    System.out.println(ste);

【讨论】:

java 在 Throwable 上定义了一个 printStackTrace 方法,你可以这样做:new Exception().printStackTrace(System.out) 我记得,for 循环的开销可能更少,但无论如何你应该只将它用作调试的东西。 . 我最喜欢这个,因为你有机会通过包装你的 println 语句来去除噪音,例如for (StackTraceElement ste : Thread.currentThread().getStackTrace()) if(ste.toString().contains("mypackages")) System.out.println(ste); 【参考方案17】:

我有一个实用方法,它返回一个带有堆栈跟踪的字符串:

static String getStackTrace(Throwable t) 
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw, true);
    t.printStackTrace(pw);
    pw.flush();
    sw.flush();
    return sw.toString();

然后像...一样登录

... 
catch (FileNotFoundException e) 
    logger.config(getStackTrace(e));

【讨论】:

这看起来像是 Netbeans 自动生成的。 是 logger 内置的 logger 还是您创建并写入文件的记录器。 @Doug Hauf,logger 是对内置 Java Logger 的引用。确保您配置了 logging.properties 文件。像这样在类的开头添加它: private static final Logger logger = Logger.getLogger( Your_Class_Name.class.getName() ); java.util.logging.Logger#log(Level, String, Throwable) 已经这样做了。这是在不必要地重写 Java 标准库中已经存在的东西,并将新开发人员指向错误的方向,即远离文档。现在通过这样做,您已经强制 Stacktrace 以某种方式格式化,其中 logging API 允许您在指定时动态改变日志语句的格式。据我所知,log4j 也有类似的行为。不要重新发明***,因为你最终会失去我已经解释过的东西。 它也存在于 2012 年。不幸的是,日期对您的情况没有影响。请参阅 [链接](docs.oracle.com/javase/1.5.0/docs/api/java/util/logging/Logger.html#log(java.util.logging.Level, java.lang.String, java.lang .Throwable))、[链接](docs.oracle.com/javase/1.5.0/docs/api/java/util/logging/Logger.html#log(java.util.logging.LogRecord))等(那里有几个函数将Throwable 传递给Handlers 到2012 年的Formatters,这是Java7,比我在这里列出的要多。而且这些函数的历史比Java7 存在的时间要长得多;因此 J2SE 5.0 javadocs)【参考方案18】:

要获取所有线程的堆栈跟踪,您可以使用 jstack 实用程序 JConsole 或发送 kill -quit 信号(在 Posix 操作系统上)。

但是,如果您想以编程方式执行此操作,您可以尝试使用 ThreadMXBean:

ThreadMXBean bean = ManagementFactory.getThreadMXBean();
ThreadInfo[] infos = bean.dumpAllThreads(true, true);

for (ThreadInfo info : infos) 
  StackTraceElement[] elems = info.getStackTrace();
  // Print out elements, etc.

如前所述,如果您只想要当前线程的堆栈跟踪,那会容易得多 - 只需使用 Thread.currentThread().getStackTrace()

【讨论】:

这个答案中的代码 sn-p 最接近以编程方式生成您在向 JVM 发送 QUIT 信号(在类似 Unix 的系统上)或在 Windows 上看到的那种转储。与其他答案不同,您可以看到监视器和同步器以及堆栈跟踪(例如,如果您遍历 StackTraceElement 数组并在每个数组上执行System.err.println(elems[i]))。 sn-p 中的第二行在dumpAllThreads 之前缺少bean.,但我想大多数人都可以解决这个问题。【参考方案19】:
Thread.currentThread().getStackTrace();

从 JDK1.5 开始可用。

对于旧版本,您可以将exception.printStackTrace() 重定向到StringWriter()

StringWriter sw = new StringWriter();
new Throwable("").printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();

【讨论】:

new Throwable().getStackTrace() 从 JDK1.4 开始可用...【参考方案20】:

傻了,我是Thread.currentThread().getStackTrace();

【讨论】:

【参考方案21】:
Thread.currentThread().getStackTrace();

如果您不在乎堆栈的第一个元素是什么,那也没关系。

new Throwable().getStackTrace();

如果这很重要,将为您当前的方法定义一个位置。

【讨论】:

(new Throwable()).getStackTrace() 的执行速度也更快(参见bugs.sun.com/bugdatabase/view_bug.do?bug_id=6375302) 您能否更详细地解释一下 Thread.currentThread().getStackTrace() 的结果与 new Throwable().getStackTrace() 的结果有何不同?我两种都试过了,发现 Thread.currentThread().getStackTrace() 返回一个数组, Thread.getStackTrace 在索引 0 中,调用方法在索引 1 处。新的 Throwable().getStackTrace() 方法返回一个数组,调用索引 0 处的方法。似乎这两种方法都为当前方法定义了位置,但位置不同。您还指出了其他不同之处吗? @Ryan,不能保证 Thread.currentThread().getStackTrace() 不承诺在不同版本的方法中保持该位置。因此,当您碰巧检查它位于索引 1 时。在 Sun 的某些版本的 JVM(1.5 或 1.6,不记得)上,它是索引 2。这就是我所说的定义位置的意思——规范要求它会在那里,并在未来留在那里。 @MightyE 该错误现在报告为已在 Java 6 中修复。查看内部结构,当请求的堆栈跟踪位于当前线程上时,它现在看起来像 (new Exception()).getStackTrace()(这将Thread.currentThread().getStackTrace(); 总是如此 @MJustin:该错误并未“现在”报告为已修复,甚至在 MightyE 撰写评论之前已修复六年。事实上,它甚至在 *** 成立之前就已经修复了……【参考方案22】:
try 

catch(Exception e) 
    StackTraceElement[] traceElements = e.getStackTrace();
    //...

Thread.currentThread().getStackTrace()

【讨论】:

您的***示例不会只为您提供与 try/catch 块相关的堆栈跟踪吗? 两者有什么区别

以上是关于如何在 Java 中获取当前堆栈跟踪?的主要内容,如果未能解决你的问题,请参考以下文章

如何在Dart中获取当前的堆栈跟踪以获得Completer.CompleteException(exception,stackTrace);

如何在Processing debugger中获取堆栈跟踪

如何在 .NET 中毫无例外地打印当前的堆栈跟踪?

如何在 Ruby 中获取堆栈跟踪对象?

如何在 Visual Studio 2017 中获取堆栈跟踪

如何在python中获取嵌套异常的堆栈跟踪?