Learning Clojure #8: Printing last stack trace in REPL

3

Occasionally you’ll hit an exception when calling into Java code and the REPL will tell you the message but not the stack trace:

user=> (java.util.Date. "foo")
java.lang.IllegalArgumentException (NO_SOURCE_FILE:0)

The last exception is stored in *e by the REPL and you can use the Clojure stacktrace API to examine it more closely:

user=> (use 'clojure.stacktrace)                                               
nil
user=> (print-stack-trace *e 5)
clojure.lang.Compiler$CompilerException: java.lang.IllegalArgumentException (NO_SOURCE_FILE:0)
 at clojure.lang.Compiler.eval (Compiler.java:4658)
    clojure.core/eval (core.clj:2035)
    clojure.main$repl__7403$read_eval_print__7415.invoke (main.clj:183)
    clojure.main$repl__7403.doInvoke (main.clj:200)
    clojure.lang.RestFn.invoke (RestFn.java:426)

Here I dumped just the top 5 frames of the top-level exception. But I really want the cause of the exception not the wrapper exception created in the compiler. You can dump all the traces in the chain like this:

user=> (print-cause-trace *e 3)
clojure.lang.Compiler$CompilerException: java.lang.IllegalArgumentException (NO_SOURCE_FILE:0)
 at clojure.lang.Compiler.eval (Compiler.java:4658)
    clojure.core/eval (core.clj:2035)
    clojure.main$repl__7403$read_eval_print__7415.invoke (main.clj:183)
Caused by: java.lang.IllegalArgumentException: null
 at java.util.Date.parse (Date.java:598)
    java.util.Date. (Date.java:255)
    user/eval (NO_SOURCE_FILE:225)

Or print only the root cause like this:

user=> (print-stack-trace (root-cause *e) 3) 
java.lang.IllegalArgumentException: null
 at java.util.Date.parse (Date.java:598)
    java.util.Date. (Date.java:255)
    user/eval (NO_SOURCE_FILE:225)

Of course you can always just treat the *e as a Throwable and call normal Java methods on it too:

user=> (.printStackTrace *e)  
java.lang.IllegalArgumentException (NO_SOURCE_FILE:0)
        at clojure.lang.Compiler.eval(Compiler.java:4658)
        at clojure.core$eval__5254.invoke(core.clj:2035)
        at clojure.main$repl__7403$read_eval_print__7415.invoke(main.clj:183)
        at clojure.main$repl__7403.doInvoke(main.clj:200)
        at clojure.lang.RestFn.invoke(RestFn.java:426)
        at clojure.main$repl_opt__7449.invoke(main.clj:254)
        at clojure.main$main__7484.doInvoke(main.clj:341)
        at clojure.lang.RestFn.invoke(RestFn.java:402)
        at clojure.lang.Var.invoke(Var.java:355)
        at clojure.lang.AFn.applyToHelper(AFn.java:171)
        at clojure.lang.Var.applyTo(Var.java:476)
        at clojure.main.main(main.java:37)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at jline.ConsoleRunner.main(ConsoleRunner.java:69)
Caused by: java.lang.IllegalArgumentException
        at java.util.Date.parse(Date.java:598)
        at java.util.Date.(Date.java:255)
        at user$eval__362.invoke(NO_SOURCE_FILE:249)
        at clojure.lang.Compiler.eval(Compiler.java:4642)
        ... 16 more

Happy hunting!

Comments

3 Responses to “Learning Clojure #8: Printing last stack trace in REPL”
  1. Joseph Hirn says:

    Hey Alex. Thanks for these little micro blogs on clojure, they’re pretty helpful.

    I’m curious what you using as your repl or primary editor? I’ve really taken to emacs/slime setup, even though it’s my first real attempt at using emacs and that has been extra effort while learning clojure.

  2. Alex says:

    Hey Joe, I’m mostly using TextMate right now (and a separate repl). I’ve been trying to get emacs/slime/clojure set up but haven’t been terribly successful so far. I would definitely like to get into using that as my primary.

  3. Joseph Hirn says:

    I’ve been struggling on my Ubuntu machine to get it all configured but on Windows Clojure Box is a blessing.