简体   繁体   English

如何从clojure期货的错误中获得堆栈跟踪?

[英]How do I get stacktraces from errors in clojure futures?

I have a few tasks that are quite independent that I've spun off using futures. 我有一些非常独立的任务,我已经使用期货分拆了。 These tasks communicate certain events back to the main app over a core.async/chan, or just talk to the db. 这些任务通过core.async / chan将某些事件传递回主应用程序,或者只是与数据库通信。

Some of these futures are now failing silently. 其中一些未来现在无声无息。 I get no stacktraces in my logs, or on std{out,err}. 我的日志中没有堆栈跟踪,或者在std {out,err}上没有堆栈跟踪。 I've tried surrounding the code in the fns called by the futures with 我已经尝试围绕期货所调用的fns中的代码

(try (do-stuff)
  (catch Exception e
    (log/error e))

just to get some output into my logs, but that--surprisingly!--didn't work. 只是为了得到一些输出到我的日志,但是 - 令人惊讶! - 没有用。

Is my only option to spin up yet another thread which does the following in a loop? 我唯一的选择是启动另一个在循环中执行以下操作的线程吗?

(let [m (Thread/getAllStackTraces)]
    (doseq [e (.entrySet m)]
      (log/error (.toString (.getKey e)))
      (doseq [s (.getValue e)]
        (log/error " " (.toString s)))))

Is this a symptom indicating that I shouldn't be using futures at all? 这是否表明我根本不应该使用期货? Should I be using agents, even though there's no need to send any messages to these agents? 我是否应该使用代理,即使不需要向这些代理发送任何消息?

The behavior is very similar to Java Future . 该行为与Java Future非常相似。 Inside the future block exceptions may be thrown and caught, and that behaves as you would expect. 在未来的块中,异常可能会被抛出并被捕获,并且行为与您期望的一样。 When an exception is not caught, the Future has no way of rethrowing it on the calling thread. 当没有捕获到异常时, Future无法在调用线程上重新抛出它。 It only does so in form of ExecutionException when you actually get its value. 它只在您实际获取其值时以ExecutionException形式ExecutionException This corresponds to deref in Clojure. 这对应于Clojure中的deref。

Let's create a function that throws something: 让我们创建一个抛出某些东西的函数:

(defn die [] (throw (RuntimeException.)))

If I just wrap it in future, it works fine: 如果我将来包装它,它可以正常工作:

user=> (def x (future (die)))
#'user/x
; Note: No exception here
user=> @x
RuntimeException   user/die (NO_SOURCE_FILE:1)
; Bam! Exception thrown on deref, think of it as
; ExecutionException when getting failed future's value in Java

So you can catch this exception on deref: 所以你可以在deref上捕获这个异常:

user=> (def x (future (die)))
#'user/x
(try @x (catch Exception e (println "Caught ya")))
Caught ya
nil

Or you can catch it inside the future: 或者你可以在未来发现它:

user=> (def x 
  #_=>   (future 
  #_=>     (try (die)
  #_=>       (catch Exception e
  #_=>         (print "Caught ya!")
  #_=>         "Something"))))
#'user/x
Caught ya
user=> @x
"Something"

Note how in this case it prints "Caught ya" immediately when the error occurs on the background thread, before deref. 注意在这种情况下如何在deref之前在后台线程上发生错误时立即打印“Caught ya”。 Then on deref it returns value returned by the catch block in future. 然后在deref上返回catch块将来返回的值。

Once again, the bottom line is - it works pretty much the same as Java futures. 最重要的是,底线是 - 它的工作方式与Java期货几乎相同。

This issue is actually tackled by Stuart Sierra here . 这个问题实际上是由Stuart Sierra 在这里解决的。 Go there and read it as it's worth it. 去那里读它,因为它是值得的。 In short, his elegant solution is to set default uncaught exception handler: 简而言之,他优雅的解决方案是设置默认的未捕获异常处理程序:

;; Assuming require [clojure.tools.logging :as log]
(Thread/setDefaultUncaughtExceptionHandler
  (reify Thread$UncaughtExceptionHandler
    (uncaughtException [_ thread ex]
      (log/error ex "Uncaught exception on" (.getName thread)))))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM