-
Notifications
You must be signed in to change notification settings - Fork 108
Stacktraces and Error Reporting
Exceptions thrown while executing Clojure code can have daunting stacktraces. The default string representations of these stacktraces are generated automatically by the CLR Exception class, and are therefore out of our hands. However, we do provide optional facilities to reformat how they print out in the REPL.
Unformatted, the Exception thrown by (/ 1 0)
prints out like this:
user=> (/ 1 0)
System.Exception: carrier ---> System.ArithmeticException: Divide by zero
at clojure.lang.Numbers.divide (System.Object x, System.Object y) [0x00024] in <138b9d0b0b3f4982b42abdb2b77eda11>:0
at clojure.lang.Numbers.divide (System.Int64 x, System.Int64 y) [0x00000] in <138b9d0b0b3f4982b42abdb2b77eda11>:0
at user$eval__94__99.invokeStatic () [0x00000] in <f57ab1e994bd43aa943a477771c387b2>:0
at user$eval__94__99.invoke () [0x00000] in <f57ab1e994bd43aa943a477771c387b2>:0
at clojure.lang.Compiler.eval (System.Object form) [0x001c2] in <138b9d0b0b3f4982b42abdb2b77eda11>:0
at clojure.lang.Compiler.eval (System.Object form) [0x00149] in <138b9d0b0b3f4982b42abdb2b77eda11>:0
at clojure$core$eval__22334.invokeStatic (System.Object ) [0x00000] in <0f47ef55b6284c9090d7b5b9ba53d099>:0
at clojure$core$eval__22334.invoke (System.Object ) [0x00000] in <0f47ef55b6284c9090d7b5b9ba53d099>:0
at arcadia$socket_repl$game_thread_evalfn__22511fn__22516__22528.invoke () [0x00011] in <a9b62fd8e501431b8ff830f23b301cf1>:0
--- End of inner exception stack trace ---
at arcadia$socket_repl$game_thread_eval__22536.invokeStatic (System.Object , System.Object ) [0x0051c] in <a9b62fd8e501431b8ff830f23b301cf1>:0
at arcadia$socket_repl$game_thread_eval__22536.invoke (System.Object , System.Object ) [0x00000] in <a9b62fd8e501431b8ff830f23b301cf1>:0
at arcadia$socket_repl$game_thread_eval__22536.invokeStatic (System.Object ) [0x0000a] in <a9b62fd8e501431b8ff830f23b301cf1>:0
at arcadia$socket_repl$game_thread_eval__22536.invoke (System.Object ) [0x00000] in <a9b62fd8e501431b8ff830f23b301cf1>:0
at clojure.lang.Var.invoke (System.Object arg1) [0x00010] in <138b9d0b0b3f4982b42abdb2b77eda11>:0
at clojure$main$replread_eval_print__32312fn__32325__32329.invoke () [0x00000] in <401b91c732a5448f8964a5c487b9d869>:0
at clojure$main$replread_eval_print__32312__32332.invoke () [0x00124] in <401b91c732a5448f8964a5c487b9d869>:0
With Arcadia's default formatting enabled, it prints like this:
user=> (/ 1 0)
System.ArithmeticException: Divide by zero
clojure.lang.Numbers.divide (object, object)
clojure.lang.Numbers.divide (System.Int64, System.Int64)
* user/eval
clojure.lang.Compiler.eval (object)
clojure.lang.Compiler.eval (object)
* clojure/core/eval
* arcadia/socket-repl/game-thread-evalfnfn
To take another example, say we have the following definitions:
(defn f5 [x y z]
(throw (InvalidOperationException. "Exception from f5")))
(defn f4 [x y z]
(f5 x y z))
(defn f3 [x y z]
(f4 x y z))
(defn f2 [x y z]
(f3 x y z))
(defn f1 [x y z]
(f2 x y z))
With formatting turned off, (f1 :a :b :c)
prints as follows:
user=> (f1 :a :b :c)
System.Exception: carrier ---> System.InvalidOperationException: Exception from f5
at user$f5__105.invokeStatic (System.Object , System.Object , System.Object ) [0x00000] in <f57ab1e994bd43aa943a477771c387b2>:0
at user$f5__105.invoke (System.Object , System.Object , System.Object ) [0x00000] in <f57ab1e994bd43aa943a477771c387b2>:0
at user$f4__111.invokeStatic (System.Object , System.Object , System.Object ) [0x0000a] in <f57ab1e994bd43aa943a477771c387b2>:0
at user$f4__111.invoke (System.Object , System.Object , System.Object ) [0x00000] in <f57ab1e994bd43aa943a477771c387b2>:0
at user$f3__117.invokeStatic (System.Object , System.Object , System.Object ) [0x0000a] in <f57ab1e994bd43aa943a477771c387b2>:0
at user$f3__117.invoke (System.Object , System.Object , System.Object ) [0x00000] in <f57ab1e994bd43aa943a477771c387b2>:0
at user$f2__123.invokeStatic (System.Object , System.Object , System.Object ) [0x0000a] in <f57ab1e994bd43aa943a477771c387b2>:0
at user$f2__123.invoke (System.Object , System.Object , System.Object ) [0x00000] in <f57ab1e994bd43aa943a477771c387b2>:0
at user$f1__129.invokeStatic (System.Object , System.Object , System.Object ) [0x0000a] in <f57ab1e994bd43aa943a477771c387b2>:0
at user$f1__129.invoke (System.Object , System.Object , System.Object ) [0x00000] in <f57ab1e994bd43aa943a477771c387b2>:0
at user$eval__139__144.invokeStatic () [0x0000a] in <f57ab1e994bd43aa943a477771c387b2>:0
at user$eval__139__144.invoke () [0x00000] in <f57ab1e994bd43aa943a477771c387b2>:0
at clojure.lang.Compiler.eval (System.Object form) [0x001c2] in <138b9d0b0b3f4982b42abdb2b77eda11>:0
at clojure.lang.Compiler.eval (System.Object form) [0x00149] in <138b9d0b0b3f4982b42abdb2b77eda11>:0
at clojure$core$eval__22334.invokeStatic (System.Object ) [0x00000] in <0f47ef55b6284c9090d7b5b9ba53d099>:0
at clojure$core$eval__22334.invoke (System.Object ) [0x00000] in <0f47ef55b6284c9090d7b5b9ba53d099>:0
at arcadia$socket_repl$game_thread_evalfn__22511fn__22516__22528.invoke () [0x00011] in <a9b62fd8e501431b8ff830f23b301cf1>:0
--- End of inner exception stack trace ---
at arcadia$socket_repl$game_thread_eval__22536.invokeStatic (System.Object , System.Object ) [0x0051c] in <a9b62fd8e501431b8ff830f23b301cf1>:0
at arcadia$socket_repl$game_thread_eval__22536.invoke (System.Object , System.Object ) [0x00000] in <a9b62fd8e501431b8ff830f23b301cf1>:0
at arcadia$socket_repl$game_thread_eval__22536.invokeStatic (System.Object ) [0x0000a] in <a9b62fd8e501431b8ff830f23b301cf1>:0
at arcadia$socket_repl$game_thread_eval__22536.invoke (System.Object ) [0x00000] in <a9b62fd8e501431b8ff830f23b301cf1>:0
at clojure.lang.Var.invoke (System.Object arg1) [0x00010] in <138b9d0b0b3f4982b42abdb2b77eda11>:0
at clojure$main$replread_eval_print__32312fn__32325__32329.invoke () [0x00000] in <401b91c732a5448f8964a5c487b9d869>:0
at clojure$main$replread_eval_print__32312__32332.invoke () [0x00124] in <401b91c732a5448f8964a5c487b9d869>:0
With formatting turned on:
user=> (f1 :a :b :c)
System.InvalidOperationException: Exception from f5
* user/f5
* user/f4
* user/f3
* user/f2
* user/f1
* user/eval
clojure.lang.Compiler.eval (object)
clojure.lang.Compiler.eval (object)
* clojure/core/eval
* arcadia/socket-repl/game-thread-evalfnfn
Formatting options are controlled by the :error-options
key in Assets/configuration.edn
, which should have a
map value. This map will be merged into the following default map:
{:collapse-invocations true,
:demunge-classnames true,
:format true, ;; if set to false, disables all formatting!
:hide-outer-exceptions true,
:star-ifn true,
:ditto-classnames false,
:hide-ifn-parameters false,
:hide-parameters false,
:quiet-invocations false}
Descriptions of these options follow.
Default value: true
Collapses consecutive stackframes corresponding to internal invocation methods of the same class implementing clojure.lang.IFn
into a single line.
Internally, Clojure functions are instances of classes that implement the clojure.lang.IFn
interface. Invocation is handled by methods such as invoke
, invokeStatic
, doInvoke
, and applyTo
, depending on context, often called in succession. This can greatly bloat stacktraces, and is rarely informative, since from the user's perspective the details of these internal method calls are usually irrelevant. :collapse-invocations
will collapse these stackframes into a single line, and (for now) not display any parameters.
Note that this may obfuscate recursion.
Default value: true
Types generated by the Clojure compiler (such as IFn
classes generated by defn
) often have names that are difficult to read, and that do not straightforwardly correspond to names they were associated with in code. :demunge-classnames
formats these generated type names in a more legible manner.
Default value: true
If set to false
, disables all formatting. :format
takes precedence over any other options.
Default value: true
If set to true
, only the innermost exception will be displayed.
Default value: true
If set to true
, stackframes generated by IFn
instances will be marked by an asterisk (*
).
Default value: false
If set to true
, redundant classname information on successive lines of stacktraces will be omitted.
Default value: false
If set to true
, the parameters of any stackframe the declaring class of which implements IFn
will be hidden. These parameters will also be hidden if :collapse-invocations
is true
or :hide-parameters
is true
.
Default value: false
If set to true
, the parameters of methods corresponding to stackframes will not be displayed.
Default value: false
If set to true
, the names of internal invocation methods such as invoke
and invokeStatic
for stackframes with declaring classes that implement IFn
will not be displayed. These names will also not be displayed if :collapse-invocations
is set to true
.