简体   繁体   English

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

[英]Determining what line printed a stack trace?

I'm seeing an errant stack trace being printed to System.err that I'd like to get rid of, but I'm having trouble identifying the line that's calling printStackTrace() . 我看到一个错误的堆栈跟踪打印到System.err我想摆脱,但我无法识别调用printStackTrace() Is there a clever way to figure out what line is making that call? 是否有一种聪明的方法来确定哪一行正在进行该呼叫?

In particular, I can't tell yet whether the call is being made in my code, or in a library. 特别是,我还无法判断是在我的代码中还是在库中进行调用。 Even narrowing down where to search -- my code or someone else's code -- would be useful. 甚至缩小搜索范围 - 我的代码或其他人的代码 - 也会很有用。

EDIT : To be clear, I'm looking for the line that is calling printStackTrace() , not the line that threw the Exception whose stack trace is being printed. 编辑 :要清楚,我正在寻找调用printStackTrace()的行,而不是抛出正在打印堆栈跟踪的Exception的行。 (The answer to the former is... well... the stack trace. :) I've looked in all the obvious places by going through the stack trace and looking for likely printStackTrace() calls at every step and found nothing. (前者的答案是......好吧......堆栈跟踪。:)我通过遍历堆栈跟踪查找所有显而易见的地方,并在每一步寻找可能的printStackTrace()调用,什么都没发现。 Either [a] the call is there and I'm an idiot (certainly a possibility), or [b] the Exception is getting passed around and printed elsewhere. 要么[a]呼叫在那里而且我是一个白痴(当然有可能),或者[b] Exception被传递并在其他地方打印。 This is why I'm having such trouble finding the printStackTrace() call; 这就是为什么我在查找printStackTrace()调用时遇到这样的麻烦; the printStackTrace() call appears to be happening "far away" from the code that's throw ing the Exception . printStackTrace()调用似乎发生在“远离” throw Exception的代码中。

EDIT : Monitoring output to System.err was a brilliant suggestion and works well. 编辑 :监视输出到System.err是一个很好的建议,并且运行良好。 Here's what I tried that worked: 这是我尝试过的工作方式:

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);
    }
}));

Here, the message I was monitoring for was "Socket closed" , which appeared in the Exception message. 在这里,我正在监视的消息是"Socket closed" ,它出现在Exception消息中。 I got a bit lucky that (a) the underlying code was ultimately calling through write(byte[],int,int) and not write(int) , and (b) the chunks didn't split the message I was monitoring for across different calls. 我有点幸运的是(a)底层代码最终通过write(byte[],int,int)调用而不是write(int) ,并且(b)块没有拆分我监视的消息不同的电话。 However, that being said, this worked a charm. 然而,话虽这么说,这有点魅力。 Thanks for the help, all! 谢谢大家的帮助!

You can provide new values for System.err and System.out, for instance wrapping the original values. 您可以为System.err和System.out提供新值,例如包装原始值。

You can then test for \\n in the new values, and either set a break point there or programmatically look at the call stack. 然后,您可以在新值中测试\\ n,并在那里设置断点或以编程方式查看调用堆栈。

You most likely want to disable normal logging while doing this. 您最有可能希望在执行此操作时禁用正常日志记录。

If you're able to reproduce it, you could simply call System.setErr() at the beginning of the program and pass a custom stream that logs every call made on the stream, with a stack trace, in order to be able to find who is printing to System.err . 如果你能够重现它,你可以简单地在程序开头调用System.setErr()并传递一个自定义流,它记录流上的每个调用,带有堆栈跟踪,以便能够找到谁正在打印到System.err It could even be smart and only log the calls when a certain keyword, part of the errant stack trace, is printed. 它甚至可以是智能的,只在打印某个关键字(错误的堆栈跟踪的一部分)时记录调用。

The first stack print the line which is enclosed by a try, look for the associated catch: the print is here. 第一个堆栈打印由try包围的行,查找相关的catch:print在这里。

If the caught exception is passed out to another method as an ordinary argument to be printed, it's here too. 如果捕获的异常作为要打印的普通参数传递给另一个方法,那么它也在这里。 With any modern IDE, like Eclipse, it's possible to follow the code and/or the types. 使用任何现代IDE,如Eclipse,可以遵循代码和/或类型。

More code about your problem help us to try to help you... 有关您的问题的更多代码有助于我们帮助您...

Here is a general-purpose class for debugging errant stream output. 这是一个用于调试错误流输出的通用类。 Mostly just copied from sigpwned's code and Thorbjorn's idea, but with a friendly API. 主要是从sigpwned的代码和Thorbjorn的想法中复制,但使用友好的API。 Also, traces are printed with a prefix on each line, so you can distinguish between the stacktrace of the call that created the exception and the stacktrace of the line that is printing the stacktrace of the exception . 此外,在每行上打印带有前缀的跟踪,因此您可以区分创建异常的调用堆栈跟踪和打印异常 堆栈跟踪的行的堆栈跟踪。

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);
            }
        });
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM