如何增加 Java 堆栈跟踪转储的显示行数?

有没有办法让 Throwable.printStackTrace(PrintStream s)打印完整的堆栈跟踪,这样我就可以看到超出 "... 40 more"的最后一行?

40120 次浏览

You don't need to; that information is present elsewhere in the stack trace. From the docs of printStackTrace():

Note the presence of lines containing the characters "...". These lines indicate that the remainder of the stack trace for this exception matches the indicated number of frames from the bottom of the stack trace of the exception that was caused by this exception (the "enclosing" exception).

This shorthand can greatly reduce the length of the output in the common case where a wrapped exception is thrown from same method as the "causative exception" is caught.

In other words, the "... x more" only appears on a chained exception, and only when the last x lines of the stack trace are already present as part of another chained exception's stack trace.

Suppose that a method catches exception Foo, wraps it in exception Bar, and throws Bar. Then Foo's stack trace will be shortened. If you for some reason want the full trace, all you need to do is take the last line before the ... in Foo's stack trace and look for it in the Bar's stack trace; everything below that line is exactly what would have been printed in Foo's stack trace.

Quick guess at a method for you.

static void printLongerTrace(Throwable t){
for(StackTraceElement e: t.getStackTrace())
System.out.println(e);
}

Let's take the stack trace from the documentation of Throwable.printStackTrace():

HighLevelException: MidLevelException: LowLevelException
at Junk.a(Junk.java:13)
at Junk.main(Junk.java:4)
Caused by: MidLevelException: LowLevelException
at Junk.c(Junk.java:23)
at Junk.b(Junk.java:17)
at Junk.a(Junk.java:11)
... 1 more
Caused by: LowLevelException
at Junk.e(Junk.java:30)
at Junk.d(Junk.java:27)
at Junk.c(Junk.java:21)
... 3 more

The causes are displayed from the most nested one at the bottom (the "root cause"), to the one which the printed stack trace belongs to.

In this case the root cause is LowLevelException, which caused MidLevelException, which caused HighLevelException.

To get the complete stack trace you have to look at the frames of the enclosing exception (and its enclosing exceptions):

  1. Look at how many frames were omitted: "... X more"
  2. Look for the omitted frames at the enclosing exception
    1. Look at how many frames were omitted: "... Y more"
    2. Append the first X - Y frames to the stack trace
  3. If Y > 0, repeat step 2 with it as number of omitted frames

So if we wanted to get the complete stack trace of LowLevelException we would do the following:

  1. Look at how many frames were omitted: "... 3 more"
  2. Look for the omitted frames at the enclosing exception (MidLevelException)
    1. 1 frame has been omitted ("... 1 more")
    2. Append the first 2 (3 - 1) frames to the stack trace
  3. Repeat step 2 with 1 as number of omitted frames
    1. Look at the enclosing exception of MidLevelException (HighLevelException)
    2. Append the first 1 frame to the stack trace

Your complete stack trace then looks like this:

LowLevelException
at Junk.e(Junk.java:30)
at Junk.d(Junk.java:27)
at Junk.c(Junk.java:21)
// From MidLevelException stack trace
at Junk.b(Junk.java:17)
at Junk.a(Junk.java:11)
// From HighLevelException stack trace
at Junk.main(Junk.java:4)

Side notes:

  • There might be cases where no frames are listed, e.g.:

    HighLevelException: MidLevelException
    at Junk.main(Junk.java:4)
    Caused by: MidLevelException
    ... 1 more
    

    This can happen when the cause it created in the same line: new HighLevelException(new MidLevelException()). Don't get confused by this, the approach described above still works, there are just no frames to use from the exception, continue with its enclosing one.

  • In some cases you can save yourself the counting by looking at the first frame which was not omitted (the line above ... X more). If you know which methods call the method in that line you can directly search for the callers in the frames of the enclosing exception:

    HighLevelException: MidLevelException: LowLevelException
    at Junk.c(Junk.java:29)
    at Junk.b(Junk.java:21)
    at Junk.a(Junk.java:13)
    at Junk.main(Junk.java:4)
    Caused by: MidLevelException
    // You know Junk.d is only called by Junk.b
    at Junk.d(Junk.java:35)
    ... 3 more