为什么Clojure栈跟踪这么长? [英] Why are Clojure stacktraces so long?

查看:87
本文介绍了为什么Clojure栈跟踪这么长?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

过去我写了一些玩具解释器/编译器,所以我将堆栈跟踪引用编译器源文件中的行和编译器错误。



如果我添加以下对我的compojure项目的错误函数定义:

 (defn dodgy-map [] 
{:1:2:3 })

Lein拒绝开始:

  $ lein ring server-headless 
线程main中的异常java.lang.RuntimeException:Map文字必须包含偶数个形式,编译:(one_man_wiki / views.clj :19)
at clojure.lang.Compiler.load(Compiler.java:6958)
at clojure.lang.RT.loadResourceScript(RT.java:359)
在clojure.lang。 RT.loadResourceScript(RT.java:350)
在clojure.lang.RT.load(RT.java:429)
在clojure.lang.RT.load(RT.java:400)
at clojure.core $ load $ fn__4890.invoke(core.clj:5415)
at clojure.core $ load.doInvoke(core.clj:5414)
at clojure.lang.RestFn.invoke (RestFn.java:408)
at clojure.core $ load_one.invoke(core.clj:5227)
at clojure.core $ load_lib.doInvoke(core.clj:5264)
clojure.lang.RestFn.applyTo(RestFn.java:142)
在clojure.core $ apply.invoke(core.clj:603)
在clojure.core $ load_libs.doInvoke(core.clj: 5298)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
在clojure.core $ apply.invoke(core.clj:603)
在clojure.core $ require .doInvoke(core.clj:5381)
at clojure.lang.RestFn.invoke(RestFn.java:457)
at one_man_wiki.handler $ eval1564 $ loading__4784__auto ____ 1565.invoke(handler.clj:1)
at one_man_wiki.handler $ eval1564.invoke(handler.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6511)
在clojure.lang.Compiler.eval Compiler.java:6501)
at clojure.lang.Compiler.load(Compiler.java:6952)
at clojure.lang.RT.loadResourceScript(RT.java:359)
at clojure .lang.RT.loadResourceScript(RT.java:350)
在clojure.lang.RT.load(RT.java:429)
在clojure.lang.RT.load(RT.java:400 )
at clojure.core $ load $ fn__4890.invoke(core.clj:5415)
在clojure.core $ load.doInvoke(core.clj:5414)
在clojure.lang。 RestFn.invoke(RestFn.java:408)
在clojure.core $ load_one.invoke(core.clj:5227)
在clojure.core $ load_lib.doInvoke(core.clj:5264)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
在clojure.core $ apply.invoke(core.clj:603)
在clojure.core $ load_libs.doInvoke .clj:5298)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
在clojure.core $ apply.invoke(core.clj:603)
在clojure。核心$ require.doInvoke(core.clj:5381)
在clojure.lang.RestFn.invoke(RestFn.java:421)
在用户$ eval1.invoke(NO_SOURCE_FILE:1)
at clojure.lang.Compiler.eval(Compiler.java:6511)
at clojure.lang.Compiler.eval(Compiler.java:6500)
at clojure.lang.Compiler.eval(Compiler.java :6477)
at clojure.core $ eval.invoke(core.clj:2797)
at clojure.main $ eval_opt.invoke(main.clj:297)
at clojure.main $ initialize.invoke(main.clj:316)
在clojure.main $ null_opt.invoke(main.clj:349)
在clojure.main $ main.doInvoke(main.clj:427)
at clojure.lang.RestFn.invoke(RestFn.java:421)
在clojure.lang.Var.invoke(Var.java:419)
在clojure.lang.AFn.applyToHelper(AFn .java:163)
在clojure.lang.Var.applyTo(Var.java:532)
在clojure.main.main(main.java:37)
导致:java。 lang.RuntimeException:map literal必须包含偶数个形式
at clojure.lang.Util.runtimeException(Util.java:170)
在clojure.lang.LispReader $ MapReader.invoke(LispReader.java :1071)
at clojure.lang.LispReader.readDelimitedList(LispReader.java:1126)
at clojure.lang.LispReader $ ListReader.invoke(LispReader.java:962)
在clojure。 lang.LispReader.read(LispReader.java:180)
at clojure.lang.Compiler.load(Compiler.java:6949)
... 51 more

如果我引用了不存在的变量:

 (defn no-such-variable [] 
i-dont-exist)

我得到一个同样巨大的回溯:

 线程main中的异常java.lang.RuntimeException:无法解析符号: i-dont-exist在这个上下文中,编译:(one_man_wiki / views.clj:18)
在clojure.lang.Compiler.analyze(Compiler.java:6281)
在clojure.lang.Compiler。分析(Compiler.java:6223)
at clojure.lang.Compiler $ BodyExpr $ Parser.parse(Compiler.java:5618)
在clojure.lang.Compiler $ FnMethod.parse(Compiler.java: 5054)
at clojure.lang.Compiler $ FnExpr.parse(Compiler.java:3674)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6453)
at clojure.lang .Compiler.analyze(Compiler.java:6262)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6443)
at clojure.lang.Compiler.analyze(Compiler.java:6262)
at clojure.lang.Compiler.access $ 100(Compiler.java:37)
at clojure.lang.Compiler $ DefExpr $ Parser.parse(Compiler.java:518)
at clojure.lang .Compiler.analyzeSeq(Compiler.java:6455)
at clojure.lang.Compiler.analyze(Compiler.java:6262)
at clojure.lang.Compiler.analyze(Compiler.java:6223)
at clojure.lang.Compiler.eval(Compiler.java:6515)
at clojure.lang.Compiler.load(Compiler.java:6952)
at clojure.lang.RT.loadResourceScript RT.java:359)
at clojure.lang.RT.loadResourceScript(RT.java:350)
at clojure.lang.RT.load(RT.java:429)
at clojure .lang.RT.load(RT.java:400)
at clojure.core $ load $ fn__4890.invoke(core.clj:5415)
在clojure.core $ load.doInvoke(core.clj :5414)
at clojure.lang.RestFn.invoke(RestFn.java:408)
在clojure.core $ load_one.invoke(core.clj:5227)
在clojure.core $ load_lib.doInvoke(core.clj:5264)
在clojure.lang.RestFn.applyTo(RestFn.java:142)
在clojure.core $ apply.invoke(core.clj:603)
at clojure.core $ load_libs.doInvoke(core.clj:5298)
在clojure.lang.RestFn.applyTo(RestFn.java:137)
在clojure.core $ apply.invoke .clj:603)
at clojure.core $ require.doInvoke(core.clj:5381)
在clojure.lang.RestFn.invoke(RestFn.java:457)
at one_man_wiki。处理程序$ eval1564 $ loading__4784__auto ____ 1565.invoke(handler.clj:1)
at one_man_wiki.handler $ eval1564.invoke(handler.clj:1)
at clojure.lang.Compiler.eval(Compiler.java: 6511)
at clojure.lang.Compiler.eval(Compiler.java:6501)
at clojure.lang.Compiler.load(Compiler.java:6952)
at clojure.lang.RT .loadResourceScript(RT.java:359)
at clojure.lang.RT.loadResourceScript(RT.java:350)
at clojure.lang.RT.load(RT.java:429)
在clojure.lang.RT.load(RT.java:400)
在clojure.core $ load $ fn__4890.invoke(core.clj:5415)
在clojure.core $ load.doInvoke core.clj:5414)
在clojure.lang.RestFn.invoke(RestFn.java:408)
在clojure.core $ load_one.invoke(core.clj:5227)
在clojure .core $ load_lib.doInvoke(core.clj:5264)
在clojure.lang.RestFn.applyTo(RestFn.java:142)
在clojure.core $ apply.invoke(core.clj:603 )
at clojure.core $ load_libs.doInvoke(core.clj:5298)
在clojure.lang.RestFn.applyTo(RestFn.java:137)
在clojure.core $ apply。 invoke(core.clj:603)
在clojure.core $ require.doInvoke(core.clj:5381)
在clojure.lang.RestFn.invoke(RestFn.java:421)
在用户$ eval1.invoke(NO_SOURCE_FILE:1)
在clojure.lang.Compiler.eval(Compiler.java:6511)
在clojure.lang.Compiler.eval(Compiler.java:6500)
at clojure.lang.Compiler.eval(Compiler.java:6477)
at clojure.core $ eval.invoke(core.clj:2797)
at clojure.main $ eval_opt.invoke main.clj:297)
at clojure.main $ initialize.invoke(main.clj:316)
at clojure.main $ null_opt.invoke(main.clj:349)
at clojure .main $ main.doInvoke(main.clj:427)
在clojure.lang.RestFn.invoke(RestFn.java:421)
在clojure.lang.Var.invoke(Var.java:419 )
at clojure.lang.AFn.applyToHelper(AFn.java:163)
at clojure.lang.Var.applyTo(Var.java:532)
at clojure.main.main main.java:37)
引发者:java.lang.RuntimeException:无法解析符号:i-dont-exists在此上下文中
at clojure.lang.Util.runtimeException(Util.java:170 )
at clojure.lang.Compiler.resolveIn(Compiler.java:6766)
at clojure.lang.Compiler.resolve(Compiler.java:6710)
在clojure.lang.Compiler。 analyzeSymbol(Compiler.java:6671)
at clojure.lang.Compiler.analyze(Compiler.java:6244)
...另外66个
ClojureSyntaxError 和 ClojureNameError 可以被捕获在顶级和一个简单的错误显示?



如果长回溯在某些情况下有用,为什么会被截断?



修改:我在回答中要查找的内容:


  1. 是否有情况(某些元编程

  2. (相关)是否有任何技术限制妨碍添加 ClojureSyntaxError 如上所述?是否值得我在Clojure错误跟踪器上打开一个问题? (更新:我打开了CLJ-1155

  3. Clojure程序员如何阅读这些回溯?有工具来帮助吗?

  4. 解决方案

    回复您的编号点


    1. 这是在Clojure中直接与Java库交互操作的习惯,所以获得完整的Java堆栈跟踪可以是有帮助的,如果你调用Java对象一些意想不到的或不支持的方式。


    2. 听起来像个好主意;我经常至少希望一个可设置的选项,这将允许我只看到源自我自己的代码,压缩所有底层语言层的stacktraces的部分。


    3. 我通常只是做的艰难的方式,在stacktrace上获得行,我的程序barfed,调整 clojure。部分(我通常测试每一分钟的变化,所以我有一个很好的想法什么变化导致的问题)。我使用的一些Emacs和Eclipse工具只显示实际错误,而不是整个stacktrace;我一般认为这是更有帮助。



      在2012年的Clojure / west,@chouser给了一个很好的谈话 [PDF]在堆栈跟踪的解剖,解释如何阅读他们,并介绍了一个有希望的工具,显然仍然没有看到一天的光。 / p>


    比较说Python的堆栈跟踪,我觉得很容易用户友好,我认为堆栈跟踪是一个粗糙的边缘Clojure,特别适合初学者。这部分是由于语言的托管性质,虽然我希望有改进可以不增加附带的复杂性。


    I've written some toy interpreters/compilers in the past, so I associate stacktraces referencing lines in compiler source files with compiler bugs.

    If I add the following bad function definition to my compojure project:

    (defn dodgy-map []
      {:1 :2 :3})
    

    Lein refuses to start:

    $ lein ring server-headless
    Exception in thread "main" java.lang.RuntimeException: Map literal must contain an even number of forms, compiling:(one_man_wiki/views.clj:19)
            at clojure.lang.Compiler.load(Compiler.java:6958)
            at clojure.lang.RT.loadResourceScript(RT.java:359)
            at clojure.lang.RT.loadResourceScript(RT.java:350)
            at clojure.lang.RT.load(RT.java:429)
            at clojure.lang.RT.load(RT.java:400)
            at clojure.core$load$fn__4890.invoke(core.clj:5415)
            at clojure.core$load.doInvoke(core.clj:5414)
            at clojure.lang.RestFn.invoke(RestFn.java:408)
            at clojure.core$load_one.invoke(core.clj:5227)
            at clojure.core$load_lib.doInvoke(core.clj:5264)
            at clojure.lang.RestFn.applyTo(RestFn.java:142)
            at clojure.core$apply.invoke(core.clj:603)
            at clojure.core$load_libs.doInvoke(core.clj:5298)
            at clojure.lang.RestFn.applyTo(RestFn.java:137)
            at clojure.core$apply.invoke(core.clj:603)
            at clojure.core$require.doInvoke(core.clj:5381)
            at clojure.lang.RestFn.invoke(RestFn.java:457)
            at one_man_wiki.handler$eval1564$loading__4784__auto____1565.invoke(handler.clj:1)
            at one_man_wiki.handler$eval1564.invoke(handler.clj:1)
            at clojure.lang.Compiler.eval(Compiler.java:6511)
            at clojure.lang.Compiler.eval(Compiler.java:6501)
            at clojure.lang.Compiler.load(Compiler.java:6952)
            at clojure.lang.RT.loadResourceScript(RT.java:359)
            at clojure.lang.RT.loadResourceScript(RT.java:350)
            at clojure.lang.RT.load(RT.java:429)
            at clojure.lang.RT.load(RT.java:400)
            at clojure.core$load$fn__4890.invoke(core.clj:5415)
            at clojure.core$load.doInvoke(core.clj:5414)
            at clojure.lang.RestFn.invoke(RestFn.java:408)
            at clojure.core$load_one.invoke(core.clj:5227)
            at clojure.core$load_lib.doInvoke(core.clj:5264)
            at clojure.lang.RestFn.applyTo(RestFn.java:142)
            at clojure.core$apply.invoke(core.clj:603)
            at clojure.core$load_libs.doInvoke(core.clj:5298)
            at clojure.lang.RestFn.applyTo(RestFn.java:137)
            at clojure.core$apply.invoke(core.clj:603)
            at clojure.core$require.doInvoke(core.clj:5381)
            at clojure.lang.RestFn.invoke(RestFn.java:421)
            at user$eval1.invoke(NO_SOURCE_FILE:1)
            at clojure.lang.Compiler.eval(Compiler.java:6511)
            at clojure.lang.Compiler.eval(Compiler.java:6500)
            at clojure.lang.Compiler.eval(Compiler.java:6477)
            at clojure.core$eval.invoke(core.clj:2797)
            at clojure.main$eval_opt.invoke(main.clj:297)
            at clojure.main$initialize.invoke(main.clj:316)
            at clojure.main$null_opt.invoke(main.clj:349)
            at clojure.main$main.doInvoke(main.clj:427)
            at clojure.lang.RestFn.invoke(RestFn.java:421)
            at clojure.lang.Var.invoke(Var.java:419)
            at clojure.lang.AFn.applyToHelper(AFn.java:163)
            at clojure.lang.Var.applyTo(Var.java:532)
            at clojure.main.main(main.java:37)
    Caused by: java.lang.RuntimeException: Map literal must contain an even number of forms
            at clojure.lang.Util.runtimeException(Util.java:170)
            at clojure.lang.LispReader$MapReader.invoke(LispReader.java:1071)
            at clojure.lang.LispReader.readDelimitedList(LispReader.java:1126)
            at clojure.lang.LispReader$ListReader.invoke(LispReader.java:962)
            at clojure.lang.LispReader.read(LispReader.java:180)
            at clojure.lang.Compiler.load(Compiler.java:6949)
            ... 51 more
    

    If I reference a variable that doesn't exist:

    (defn no-such-variable []
      i-dont-exist)
    

    I get an equally ginormous traceback:

    Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol: i-dont-exist in this context, compiling:(one_man_wiki/views.clj:18)
            at clojure.lang.Compiler.analyze(Compiler.java:6281)
            at clojure.lang.Compiler.analyze(Compiler.java:6223)
            at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5618)
            at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5054)
            at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3674)
            at clojure.lang.Compiler.analyzeSeq(Compiler.java:6453)
            at clojure.lang.Compiler.analyze(Compiler.java:6262)
            at clojure.lang.Compiler.analyzeSeq(Compiler.java:6443)
            at clojure.lang.Compiler.analyze(Compiler.java:6262)
            at clojure.lang.Compiler.access$100(Compiler.java:37)
            at clojure.lang.Compiler$DefExpr$Parser.parse(Compiler.java:518)
            at clojure.lang.Compiler.analyzeSeq(Compiler.java:6455)
            at clojure.lang.Compiler.analyze(Compiler.java:6262)
            at clojure.lang.Compiler.analyze(Compiler.java:6223)
            at clojure.lang.Compiler.eval(Compiler.java:6515)
            at clojure.lang.Compiler.load(Compiler.java:6952)
            at clojure.lang.RT.loadResourceScript(RT.java:359)
            at clojure.lang.RT.loadResourceScript(RT.java:350)
            at clojure.lang.RT.load(RT.java:429)
            at clojure.lang.RT.load(RT.java:400)
            at clojure.core$load$fn__4890.invoke(core.clj:5415)
            at clojure.core$load.doInvoke(core.clj:5414)
            at clojure.lang.RestFn.invoke(RestFn.java:408)
            at clojure.core$load_one.invoke(core.clj:5227)
            at clojure.core$load_lib.doInvoke(core.clj:5264)
            at clojure.lang.RestFn.applyTo(RestFn.java:142)
            at clojure.core$apply.invoke(core.clj:603)
            at clojure.core$load_libs.doInvoke(core.clj:5298)
            at clojure.lang.RestFn.applyTo(RestFn.java:137)
            at clojure.core$apply.invoke(core.clj:603)
            at clojure.core$require.doInvoke(core.clj:5381)
            at clojure.lang.RestFn.invoke(RestFn.java:457)
            at one_man_wiki.handler$eval1564$loading__4784__auto____1565.invoke(handler.clj:1)
            at one_man_wiki.handler$eval1564.invoke(handler.clj:1)
            at clojure.lang.Compiler.eval(Compiler.java:6511)
            at clojure.lang.Compiler.eval(Compiler.java:6501)
            at clojure.lang.Compiler.load(Compiler.java:6952)
            at clojure.lang.RT.loadResourceScript(RT.java:359)
            at clojure.lang.RT.loadResourceScript(RT.java:350)
            at clojure.lang.RT.load(RT.java:429)
            at clojure.lang.RT.load(RT.java:400)
            at clojure.core$load$fn__4890.invoke(core.clj:5415)
            at clojure.core$load.doInvoke(core.clj:5414)
            at clojure.lang.RestFn.invoke(RestFn.java:408)
            at clojure.core$load_one.invoke(core.clj:5227)
            at clojure.core$load_lib.doInvoke(core.clj:5264)
            at clojure.lang.RestFn.applyTo(RestFn.java:142)
            at clojure.core$apply.invoke(core.clj:603)
            at clojure.core$load_libs.doInvoke(core.clj:5298)
            at clojure.lang.RestFn.applyTo(RestFn.java:137)
            at clojure.core$apply.invoke(core.clj:603)
            at clojure.core$require.doInvoke(core.clj:5381)
            at clojure.lang.RestFn.invoke(RestFn.java:421)
            at user$eval1.invoke(NO_SOURCE_FILE:1)
            at clojure.lang.Compiler.eval(Compiler.java:6511)
            at clojure.lang.Compiler.eval(Compiler.java:6500)
            at clojure.lang.Compiler.eval(Compiler.java:6477)
            at clojure.core$eval.invoke(core.clj:2797)
            at clojure.main$eval_opt.invoke(main.clj:297)
            at clojure.main$initialize.invoke(main.clj:316)
            at clojure.main$null_opt.invoke(main.clj:349)
            at clojure.main$main.doInvoke(main.clj:427)
            at clojure.lang.RestFn.invoke(RestFn.java:421)
            at clojure.lang.Var.invoke(Var.java:419)
            at clojure.lang.AFn.applyToHelper(AFn.java:163)
            at clojure.lang.Var.applyTo(Var.java:532)
            at clojure.main.main(main.java:37)
    Caused by: java.lang.RuntimeException: Unable to resolve symbol: i-dont-exist in this context
            at clojure.lang.Util.runtimeException(Util.java:170)
            at clojure.lang.Compiler.resolveIn(Compiler.java:6766)
            at clojure.lang.Compiler.resolve(Compiler.java:6710)
            at clojure.lang.Compiler.analyzeSymbol(Compiler.java:6671)
            at clojure.lang.Compiler.analyze(Compiler.java:6244)
            ... 66 more
    

    Why doesn't the Clojure compiler raise a ClojureSyntaxError and a ClojureNameError that could be caught at the top level and a simple error shown? These are common developer errors during development.

    If the long tracebacks are useful in some circumstances, why are they truncated?

    Edit: What I'm looking for in an answer:

    1. Are there situations (some metaprogramming with Java interop perhaps?) when getting a traceback referencing clojure.lang is useful?
    2. (Related) Are there any technical constraints preventing adding a ClojureSyntaxError as I described above? Is it worth me opening an issue on the Clojure bug tracker? (update: I've opened CLJ-1155)
    3. How do experience Clojure programmers read these tracebacks? Are there tools to help? Does everyone use clj-stacktrace?

    解决方案

    Replying to your numbered points,

    1. it is idiomatic in Clojure to interoperate directly with Java libraries, so getting the full Java stacktrace can be helpful if you are calling into Java objects in some unexpected or unsupported way.

    2. Sounds like a good idea; I've often at least wished for a settable option which would allow me to see only the parts of stacktraces originating in my own code, suppressing all the underlying language layers.

    3. I usually just do it the hard way and pore over the stacktrace to get the line in my program that barfed, tuning out the clojure.* parts (and I usually test each minute change so I have a pretty good idea what change caused the problem). Some of the Emacs and Eclipse tools I've used show you only the actual error and not the whole stacktrace; I generally find this to be more helpful.

      At Clojure/west in 2012, @chouser gave a nice talk [PDF] on the anatomy of stacktraces, explained how to read them, and introduced a promising-looking tool, which apparently still has not seen the light of day yet.

    Compared with, say, Python, whose stacktraces I find pretty user-friendly, I think stacktraces are a rough edge in Clojure, particularly for beginners. This is partly due to the "hosted" nature of the language, though I expect there are improvements which could be made without adding incidental complexity.

    这篇关于为什么Clojure栈跟踪这么长?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆