Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6507809

"Caused by" notation for stack traces unnecessarily hard to read

    Details

    • Type: Enhancement
    • Status: Open
    • Priority: P3
    • Resolution: Unresolved
    • Affects Version/s: 7
    • Fix Version/s: None
    • Component/s: core-libs
    • Labels:

      Description

      The introduction of nested exceptions in JDK 1.4 meant that many kinds of software now rethrow exceptions as root causes, possibly several levels deep. While this has preserved debuggability for deeply nested problems, the stack trace format seems almost designed to confuse the reader. Taking the example from the Javadoc 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 "... 3 more" notation is tricky to interpret even after reading the Javadoc! It is an abbreviation for the raw list of causes:

      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)
              at Junk.main(Junk.java:4)
      Caused by: LowLevelException
              at Junk.e(Junk.java:30)
              at Junk.d(Junk.java:27)
              at Junk.c(Junk.java:21)
              at Junk.b(Junk.java:17)
              at Junk.a(Junk.java:11)
              at Junk.main(Junk.java:4)

      The raw list, though longer, is actually easier to interpret because you can almost ignore all but the last stack trace, which gives the actual call sequence; this sequence is broken up in the current implementation so it is hard to follow. ("Almost" because the upper stack traces contain a bit more information: where the wrapping exception was thrown from. Usually this is uninteresting, but occasionally it matters.)

      I suggest a more processed stack trace which actually shows the original call stack in logical order while preserving all the useful information of the current implementation and being a bit shorter to boot:

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

      or even more concisely:

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

      I find this format much more intuitive. It looks nearly like the stack trace would have looked if there had not been exception wrappers:

      LowLevelException
              at Junk.e(Junk.java:30)
              at Junk.d(Junk.java:27)
              at Junk.c(Junk.java:21)
              at Junk.b(Junk.java:17)
              at Junk.a(Junk.java:11)
              at Junk.main(Junk.java:4)

      and it only introduces two extra lines of text for each wrapping, showing clearly both where the original exception was caught, and where the wrapper exception was thrown. The formatting matches the developer's experience that in most cases an exception cause in Java is just a workaround for the existence of checked exceptions, which need to be "translated" to different types, or occasionally annotated with a more descriptive message.

        Attachments

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              jglick Jesse Glick (Inactive)
            • Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Imported:
                Indexed: