Pure Danger Tech


navigation
home

Learning Clojure #8: Printing last stack trace in REPL

17 Feb 2010

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.<init> (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.<init> (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.<init>(Date.java:255)
        at user$eval__362.invoke(NO_SOURCE_FILE:249)
        at clojure.lang.Compiler.eval(Compiler.java:4642)
        ... 16 more

Happy hunting!