1

我看到一个错误的堆栈跟踪被打印到System.err我想摆脱的,但我无法识别正在调用的行printStackTrace()。有没有一种聪明的方法来弄清楚是哪条线路在打这个电话?

特别是,我还不能确定调用是在我的代码中还是在库中进行的。即使缩小搜索范围——我的代码或其他人的代码——也会很有用。

编辑:为了清楚起见,我正在寻找正在调用printStackTrace()的行,而不是抛出Exception正在打印其堆栈跟踪的行。(前者的答案是……嗯……堆栈跟踪。:) 我通过堆栈跟踪查看了所有明显的地方,并printStackTrace()在每一步寻找可能的调用,但一无所获。要么 [a] 电话在那里,但我是个白痴(当然有可能),或者 [b] 电话Exception被传递并打印在其他地方。这就是为什么我很难找到printStackTrace()电话的原因;调用似乎发生在printStackTrace()“远离throwException.

编辑:监控输出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

4 回答 4

2

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

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

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

于 2013-06-08T22:00:41.683 回答
1

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

于 2013-06-08T22:00:43.357 回答
0

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

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

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

于 2013-06-08T21:57:03.033 回答
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);
            }
        });
    }
}
于 2014-01-25T02:20:38.257 回答