确定哪条线打印了堆栈跟踪?

时间:2013-06-08 21:46:54

标签: java

我看到一个错误的堆栈跟踪打印到我想要​​摆脱的System.err,但是我无法识别正在调用printStackTrace()的行。是否有一种聪明的方法来确定哪一行正在进行该呼叫?

特别是,我还无法判断是在我的代码中还是在库中进行调用。即使缩小搜索位置 - 我的代码或其他人的代码 - 也会很有用。

编辑:要清楚,我正在寻找正在调用printStackTrace()的行,而不是抛出正在打印其堆栈跟踪的Exception的行。 (前者的答案是......好吧......堆栈跟踪。:)我通过遍历堆栈跟踪查找了所有显而易见的地方,并在每一步查找可能的printStackTrace()次调用没有。要么[a]呼叫在那里而且我是一个白痴(当然有可能),或者[b] Exception正在传递并打印到其他地方。这就是为什么我在找printStackTrace()电话时遇到这样的麻烦; printStackTrace()调用似乎发生在距离throw Exception的代码“远离”的位置。

编辑:监控输出到System.err是一个很好的建议并且效果很好。以下是我尝试过的方法:

final PrintStream systemErr=System.err;
System.setErr(new PrintStream(new OutputStream() {
    @Override
    public void flush() throws IOException {
        systemErr.flush();
    }

    @Override
    public void close() throws IOException {
        systemErr.close();
    }

    @Override
    public void write(byte[] buf, int off, int len) throws IOException {
        String s=new String(buf, Charset.defaultCharset());
        if(s.contains("Socket closed"))
            new Exception().printStackTrace();
        systemErr.write(buf, off, len);
    }

    @Override
    public void write(int b) throws IOException {
        systemErr.write(b);
    }
}));

此处,我正在监控的邮件是"Socket closed",它出现在Exception邮件中。我有点幸运的是(a)底层代码最终通过write(byte[],int,int)调用而不是write(int),并且(b)块没有拆分我在不同调用中监视的消息。然而,话虽这么说,这有点魅力。谢谢你的帮助,全部!

4 个答案:

答案 0 :(得分:2)

您可以为System.err和System.out提供新值,例如包装原始值。

然后,您可以在新值中测试\ n,并在那里设置断点或以编程方式查看调用堆栈。

您很可能希望在执行此操作时禁用正常日志记录。

答案 1 :(得分:1)

如果你能够重现它,你可以简单地在程序开头调用System.setErr()并传递一个自定义流,记录每次在流上进行的调用,并使用堆栈跟踪,以便能够找到谁正在打印到System.err。它甚至可以是智能的,只有在打印出某个关键字(错误的堆栈跟踪的一部分)时才记录调用。

答案 2 :(得分:0)

第一个堆栈打印由try包围的行,查找相关的catch:打印在这里。

如果捕获的异常作为要打印的普通参数传递给另一个方法,那么它也在这里。使用任何现代IDE(如Eclipse),都可以遵循代码和/或类型。

有关您的问题的更多代码有助于我们帮助您......

答案 3 :(得分:0)

这是一个用于调试错误流输出的通用类。主要是从sigpwned的代码和Thorbjorn的想法中复制,但使用友好的API。此外,在每行上打印带有前缀的跟踪,因此您可以区分创建异常的调用的堆栈跟踪和打印异常堆栈跟踪的行的堆栈跟踪

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.Charset;

/** Utility methods for figuring out when a given string is being printed to System.out or System.err. */
public class StreamDebug {
    /** Stores the pristine System.err stream before it is clobbered by other methods. */
    private static final PrintStream PRISTINE_SYS_ERR = System.err;

    /** Dumps a stack trace if the trigger string is printed to System.out. */
    public static void dumpIfSysOutContains(String trigger) {
        System.setOut(wrapAndDumpIfContains(System.out, trigger));
    }

    /** Dumps a stack trace if the trigger string is printed to System.err. */
    public static void dumpIfSysErrContains(String trigger) {
        System.setErr(wrapAndDumpIfContains(System.err, trigger));
    }

    /**
     * When dumping the stack trace, all lines in the trace before this delimiter will be ignored.
     * This is chosen to match the "new Throwable().printStackTrace(new PrintWriter(sw));" line below.
     */
    private static final String INTERESTING_DELIMITER = "java.lang.Throwable.printStackTrace";

    /** 
     * Returns a PrintStream which will redirect all of its output to the source PrintStream.  If
     * the trigger string is passed through the wrapped PrintStream, then it will dump the
     * stack trace of the call that printed the trigger. 
     * 
     * @param source    the returned PrintStream will delegate to this stream
     * @param trigger   the string which triggers a stack dump
     * @return          a PrintStream with the above properties
     */
    public static PrintStream wrapAndDumpIfContains(final PrintStream source, final String trigger) {
        return new PrintStream(new OutputStream() {
            @Override
            public void flush() throws IOException {
                source.flush();
            }

            @Override
            public void close() throws IOException {
                source.close();
            }

            @Override
            public void write(byte[] buf, int off, int len) throws IOException {
                String s = new String(buf, off, len, Charset.defaultCharset());
                if (s.contains(trigger)) {
                    // print the triggered header
                    PRISTINE_SYS_ERR.println("+----------\\");
                    PRISTINE_SYS_ERR.println("| TRIGGERED \\");

                    // put the stack trace into an array of strings
                    StringWriter sw = new StringWriter();
                    new Throwable().printStackTrace(new PrintWriter(sw));
                    String[] lines = sw.toString().replaceAll("\\r\\n", "\n").split("\\n"); // stack trace as a string

                    // print each line of the stacktrace with a prefix to differentiate from the "standard" stream
                    // but don't print until we've gotten past the INTERESTING_DELIMITER
                    boolean foundInterestingDelimiter = false;
                    boolean pastInterestingDelimiter = false;
                    for (String line : lines) {
                        // set foundInterestingDelimiter to true when we find the delimiter
                        if (!foundInterestingDelimiter && line.contains(INTERESTING_DELIMITER)) {
                            foundInterestingDelimiter = true;
                        }
                        // set pastInterestingDelimiter to true when the line no longer contains the delimiter
                        if (foundInterestingDelimiter && !pastInterestingDelimiter && !line.contains(INTERESTING_DELIMITER)) {
                            pastInterestingDelimiter = true;
                        }
                        // only print the stack trace once we've gotten past the interesting delimiter
                        if (pastInterestingDelimiter) {
                            PRISTINE_SYS_ERR.print("| ");
                            PRISTINE_SYS_ERR.println(line.trim());
                        }
                    }

                    // print the triggered footer
                    PRISTINE_SYS_ERR.println("| TRIGGERED /");
                    PRISTINE_SYS_ERR.println("+----------/");
                }
                source.write(buf, off, len);
            }

            @Override
            public void write(int b) throws IOException {
                source.write(b);
            }
        });
    }
}