Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 3.6.7 -- UNRELEASED

Adderd a :traditional option when printing or formatting exceptions.

## 3.6.6 -- 30 Sep 2025

Improved `clj-commons.format.exceptions/parse-exception` to deal with exception text changes
Expand Down
20 changes: 12 additions & 8 deletions src/clj_commons/format/exceptions.clj
Original file line number Diff line number Diff line change
Expand Up @@ -642,11 +642,13 @@
(defn- render-exception
[exception-stack options]
(let [{show-properties? :properties
:or {show-properties? true}} options
:keys [traditional]
:or {show-properties? true
traditional *traditional*}} options
exception-font (:exception *fonts*)
message-font (:message *fonts*)
property-font (:property *fonts*)
modern? (not *traditional*)
modern? (not traditional)
max-class-name-width (max-from exception-stack #(-> % :class-name length))
message-indent (+ 2 max-class-name-width)
exception-f (fn [{:keys [class-name message properties]}]
Expand Down Expand Up @@ -678,7 +680,7 @@
"\n")
root-stack-trace (-> exception-stack last :stack-trace)]
(list
(when *traditional*
(when traditional
exceptions)

(build-stack-trace-output root-stack-trace modern?)
Expand Down Expand Up @@ -707,18 +709,20 @@
:filter | The stack frame filter, which defaults to [[*default-stack-frame-filter*]]
:properties | If true (the default) then properties of exceptions will be output
:frame-limit | If non-nil, the number of stack frames to keep when outputting the stack trace of the deepest exception
:traditional | If true, the use the traditional Java ordering of stack frames.

Output may be traditional or modern, as controlled by the :traditonal option
(which defaults to the value of [[*traditional*]]).

Output may be traditional or modern, as controlled by [[*traditional*]].
Traditional is the typical output order for Java: the stack of exceptions comes first (outermost to
innermost) followed by the stack trace of the innermost exception, with the frames
in order from deepest to most shallow.

Modern output is more readable; the stack trace comes first and is reversed: shallowest frame to most deep.
Modern output is the default: the stack trace comes first and is reversed: shallowest frame to most deep.
Then the exception stack is output, from the root exception to the outermost exception.
The modern output order is more readable, as it puts the most useful information together at the bottom, so that
it is not necessary to scroll back to see, for example, where the exception occurred.

The default is modern.
it is not necessary to scroll back to see, for example, where the exception occurred, and more sensible,
since it reflects a chronological order.

The stack frame filter is passed the map detailing each stack frame
in the stack trace, and must return one of the following values:
Expand Down
27 changes: 19 additions & 8 deletions test/clj_commons/exception_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -605,14 +605,16 @@ failed with ABC123"
(set frame-names)))))

(defn parse-and-format
[file]
(binding [*color-enabled* false]
(-> file
io/resource
slurp
(parse-exception nil)
(f/format-exception* nil)
string/split-lines)))
([file]
(parse-and-format file nil))
([file options]
(binding [*color-enabled* false]
(-> file
io/resource
slurp
(parse-exception options)
(f/format-exception* options)
string/split-lines))))

(deftest parse-no-message-exception
(is (match? ["user/x REPL Input ┐ (repeats 255 times)"
Expand All @@ -622,6 +624,15 @@ failed with ABC123"
"java.lang.StackOverflowError:"]
(parse-and-format "overflow-exception.txt"))))

(deftest parse-no-message-exception-traditional
(is (match? ["java.lang.StackOverflowError:"
""
" ..."
"user/x REPL Input ┐ (repeats 255 times)"
"user/y REPL Input ┘"
"user/x REPL Input"]
(parse-and-format "overflow-exception.txt" {:traditional true}))))

(deftest parse-nested-exception

(is (match? [" ..."
Expand Down