Recently, I fixed a bug in a library that exposed the capability to modify the data within an ex-info
before printing (logging) it, by creating a new ex-info
to store the updated data. This naturally overwrote the existing stack trace on the given ex-info
, so my fix was to use interop to set the stack trace of the new ex-info
to the stack trace of the original ex-info
:
(let [cause (process-ex-data-map ex-data-field-fn (ex-cause ex))
new-ex (ex-info (ex-message ex)
(into {} (map (fn [[k v]] {k (ex-data-field-fn v)}) (ex-data ex)))
cause)]
(.setStackTrace ^ExceptionInfo new-ex (.getStackTrace ^ExceptionInfo ex))
new-ex)
This is a fine solution, but it feels bad or awkward to be directly modifying the internals of the ex-info
. I understand that ExceptionInfo
probably can't be immutable for interop reasons, but I think it would be helpful if there were functions in clojure.core
that operated on ExceptionInfo
s as if they were immutable.
Maybe something like this?
(defn ex-stacktrace
([ex]
(when (instance? Throwable ex)
(.getStackTrace ^Throwable ex)))
([ex other]
(when (and (instance? clojure.lang.ExceptionInfo ex)
(instance? Throwable other))
(let [new-ex (ex-info (ex-message ex) (ex-data ex) (ex-cause ex))]
(.setStackTrace ^clojure.lang.ExceptionInfo new-ex
(.getStackTrace ^Throwable other))
new-ex))))