-
Notifications
You must be signed in to change notification settings - Fork 29
Description
I have a pretty common scenario in my code base that I have had a hard time getting slingshot to handle properly. Not sure if there is already some code that solves this problem or if something could/should be added to make my scenario easier.
I have an exception that occurs at a lower level in the code and as that exception bubbles up, higher levels of the code have additional error context to add. I would like to assoc more information into the map at each of the higher level points without increasing the depth of the exception stack (and possibly not changing the error message). A pretty trivial example is below:
(defn stacktrace-depth
([t] (stacktrace-depth 1 t))
([i t]
(if (.getCause t)
(recur (inc i) (.getCause t))
i)))
(defn stacking-up []
(try+
(try+
(try+
(try+
(throw (RuntimeException. "Something went wrong"))
(catch RuntimeException e
(throw+ {:some-info "here"
:type 1}
(.getMessage e))))
(catch [:type 1] {:as error-stuff}
(throw+ (assoc error-stuff :more-info "over there")
(.getMessage (:cause &throw-context)))))
(catch [:type 1] {:as error-stuff}
(throw+ (assoc error-stuff :one-last "thing")
(.getMessage (:cause &throw-context)))))
(catch (constantly true) {:as err-ctx}
(println "Exception Depth: "(stacktrace-depth (:throwable &throw-context)))
(println "Error message: " (.getMessage (:throwable &throw-context)))
(println "Error map: " err-ctx))))
=> (stacking-up)
;; Exception Depth: 4
;; Error message: Something went wrong
;; Error map: {:one-last thing, :more-info over there, :some-info here, :type 1}The above has:
ExceptionInfo
-->ExceptionInfo
----> ExceptionInfo
------> RuntimeException
Really all I wanted was the additional information (i.e. the :more-info, :one-last, etc from above). It's more difficult to find out the real error now and I have to be careful about the error message (make sure I drag it along with me from the lower level exception).
To make this easier, I created a new macro I called merge-throw+:
(defn wrapped? [^Throwable t]
(:slingshot.support/wrapper? (meta (ex-data t))))
(defmacro merge-throw+
[new-obj & [message]]
`(let [{throwable# :throwable cause# :cause object# :object} ~'&throw-context]
(throw
(wrap
{:message (or ~message (.getMessage throwable#))
:stack-trace (.getStackTrace throwable#)
:cause (if (wrapped? throwable#)
cause#
throwable#)
:object (if (wrapped? throwable#)
(merge object# ~new-obj)
~new-obj)}))))
(defn keeping-it-flat []
(try+
(try+
(try+
(try+
(throw (RuntimeException. "Something went wrong"))
(catch RuntimeException e
(merge-throw+ {:some-info "here"
:type 1})))
(catch [:type 1] {:as error-stuff}
(merge-throw+ {:more-info "over there"})))
(catch [:type 1] {:as error-stuff}
(merge-throw+ {:one-last "thing"})))
(catch (constantly true) {:as err-ctx}
(println "Exception Depth: "(stacktrace-depth (:throwable &throw-context)))
(println "Error message: " (.getMessage (:throwable &throw-context)))
(println "Error map: " err-ctx))))
=> (keeping-it-flat)
;; Exception Depth: 2
;; Error message: Something went wrong
;; Error map: {:one-last thing, :more-info over there, :some-info here, :type 1}Am I going about this in the right way? Is there something built in that makes what I'm wanting easier?