目前(1.10.0-RC5)堆栈跟踪看起来像这样
user=> (Exception.)
#error {
:cause nil
:via
[{:type java.lang.Exception
:message nil
:at [user$eval3 invokeStatic "NO_SOURCE_FILE" 1]}]
:trace
[[user$eval3 invokeStatic "NO_SOURCE_FILE" 1]
[user$eval3 invoke "NO_SOURCE_FILE" 1]
[clojure.lang.Compiler eval "Compiler.java" 7176]
[clojure.lang.Compiler eval "Compiler.java" 7131]
[clojure.core$eval invokeStatic "core.clj" 3214]
[clojure.core$eval invoke "core.clj" 3210]
[clojure.main$repl$read_eval_print__9068$fn__9068 invoke "main.clj" 414]
[clojure.main$repl$read_eval_print__9068 invoke "main.clj" 414]
[clojure.main$repl$fn__9068 invoke "main.clj" 435]
[clojure.main$repl invokeStatic "main.clj" 435]
[clojure.main$repl_opt invokeStatic "main.clj" 499]
[clojure.main$main invokeStatic "main.clj" 598]
[clojure.main$main doInvoke "main.clj" 561]
[clojure.lang.RestFn invoke "RestFn.java" 397]
[clojure.lang.AFn applyToHelper "AFn.java" 152]
[clojure.lang.RestFn applyTo "RestFn.java" 132]
[clojure.lang.Var applyTo "Var.java" 705]
[clojure.main main "main.java" 37]]}
这些是Java堆栈跟踪,即使是Clojure内部运行的代码也是如此。我相信许多开发人员会从以Clojure感知的方式渲染来自Clojure的堆栈跟踪元素中受益。例如
[clojure.core/eval "core.clj" 3214]
[clojure.main/repl/read-eval-print "main.clj" 414]
[clojure.main/repl "main.clj" 435]
[clojure.main/repl-opt "main.clj" 499]
[clojure.main/main "main.clj" 598]
[clojure.main/main "main.clj" 561]
主要区别
- 名称去混淆(用户看到的名字是它们在Clojure文件中显示的,而不是它们被编译成Java代码的方式。我相信并不是每个Clojure用户都需要了解那部分内容)
- 重复的调用/调用Static/执行Invoke合并(这又是实现细节,因为从Clojure用户调用方法是一个操作。因此,许多堆栈跟踪可能会变得更短)
我相信还有许多类似实现细节,例如协议调用、多重方法调用、匿名函数,编译细节会溢出到堆栈跟踪中,使得大小增加,并且只能由编译器专家阅读。虽然这些没有在这个问题中指定,但它们也应该合并到Clojure友好的表示中。
这样的表示应该应用于许多地方
- 默认异常打印程序
- clojure.main
- clojure.repl
- clojure.stacktrace(由clojure.test使用)
也许所有这些地方都需要某种统一?例如,为什么在clojure.core中有三个{{(root-cause)}}实现?
先前的工作:我相信{{(pst)}}做一些基本的去混淆,但不合并。它也不处理例如协议等的复杂情况。此外,它必须显式调用,这不太方便。