简体   繁体   中英

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() . 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. (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. 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. This is why I'm having such trouble finding the printStackTrace() call; the printStackTrace() call appears to be happening "far away" from the code that's throw ing the Exception .

EDIT : Monitoring output to System.err was a brilliant suggestion and works well. 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. 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. 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.

You can then test for \\n in the new values, and either set a break point there or programmatically look at the call stack.

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 . 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.

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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