【问题标题】:How do I get stacktraces from errors in clojure futures?如何从 clojure 期货中的错误中获取堆栈跟踪?
【发布时间】:2014-07-09 21:02:59
【问题描述】:

我有一些非常独立的任务,我使用期货分拆出来。这些任务通过 core.async/chan 将某些事件传回主应用程序,或者只是与 db 通信。

其中一些未来正在悄然失败。我的日志或 std{out,err} 上没有堆栈跟踪。我试过用

包围期货调用的 fns 中的代码
(try (do-stuff)
  (catch Exception e
    (log/error e))

只是为了在我的日志中获得一些输出,但是——令人惊讶的是!——没有用。

我唯一的选择是启动另一个循环执行以下操作的线程吗?

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

这是表明我根本不应该使用期货的症状吗?我是否应该使用代理,即使不需要向这些代理发送任何消息?

【问题讨论】:

  • 我对 core.async 不熟悉,所以我无法评论您在该库中使用期货的适当性。但是如果未来由于异常而失败,那么取消对未来的引用应该重新抛出异常。

标签: concurrency clojure


【解决方案1】:

该行为与 Java Future 非常相似。在 future 块内可能会抛出和捕获异常,这与您预期的一样。当没有捕获到异常时,Future 无法在调用线程上重新抛出它。只有当您真正获得它的价值时,它才会以ExecutionException 的形式这样做。这对应于 Clojure 中的 deref。

让我们创建一个抛出一些东西的函数:

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

如果我以后只包装它,它可以正常工作:

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

所以你可以在 deref 上捕获这个异常:

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

或者你可以在未来捕捉到它:

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

请注意,在这种情况下,当后台线程上发生错误时,它会在 deref 之前立即打印“Caught ya”。然后在 deref 上,它返回未来 catch 块返回的值。

再一次,底线是 - 它的工作原理与 Java 期货几乎相同。

【讨论】:

    【解决方案2】:

    这个问题实际上是由 Stuart Sierra here 解决的。去那里读一读,因为它是值得的。简而言之,他优雅的解决方案是设置默认的未捕获异常处理程序:

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

    【讨论】:

    • 这对期货仍然有效吗?根据文章:“另一个问题:Future 中的异常总是被 Future 捕获。在调用 Future.get(Clojure 中的 deref)之前不会抛出异常。”
    猜你喜欢
    • 2019-03-05
    • 2014-08-20
    • 1970-01-01
    • 2018-09-06
    • 1970-01-01
    • 2013-04-18
    • 1970-01-01
    • 2010-11-10
    • 1970-01-01
    相关资源
    最近更新 更多