Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Rethrowing exceptions with more data #31

Open
senior opened this Issue · 0 comments

1 participant

@senior

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.