From 3f977758f62a7ac95b41757c81eb7dad294e40d2 Mon Sep 17 00:00:00 2001 From: Moritz Heidkamp Date: Tue, 7 Nov 2023 16:47:03 +0100 Subject: [PATCH] Preserve ex-data of all ex-info exceptions in the cause chain (#61) Instead of only adding ex-data of the top-level exception into the extra map, walk down the cause chain and add all ex-data encountered therein. To prevent them from clobbering each other or any keys already present in the extra map, nest all ex-data under separate keys. The top-level exception's ex-data is added under "ex-data" while causes are added under "ex-data, cause : " where `` is the cause's position in the chain and `` is its message. --- src/sentry_clj/core.clj | 21 ++++++++++++++++++++- test/sentry_clj/core_test.clj | 13 +++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/sentry_clj/core.clj b/src/sentry_clj/core.clj index dc89900..d67e311 100644 --- a/src/sentry_clj/core.clj +++ b/src/sentry_clj/core.clj @@ -90,6 +90,25 @@ (.setOthers request (java-util-hashmappify-vals other))) request)) +(defn ^:private merge-all-ex-data + "Merges ex-data of all ex-info exceptions in the cause chain of exn into extra. + Each ex-data is added under a separate key so that they don't clobber each other." + [extra exn] + (loop [exn exn + num 0 + extra extra] + (if exn + (let [data (ex-data exn)] + (recur (ex-cause exn) + (inc num) + (cond-> extra + data + (assoc (if (zero? num) + "ex-data" + (str "ex-data, cause " num ": " (ex-message exn))) + data)))) + extra))) + (defn ^:private map->event "Converts a map into an event." ^SentryEvent @@ -130,7 +149,7 @@ (.addBreadcrumb sentry-event ^Breadcrumb breadcrumb))) (when server-name (.setServerName sentry-event server-name)) - (when-let [data (merge extra (ex-data throwable))] + (when-let [data (merge-all-ex-data extra throwable)] (doseq [[k v] (java-util-hashmappify-vals data)] (.setExtra sentry-event k v))) (when throwable diff --git a/test/sentry_clj/core_test.clj b/test/sentry_clj/core_test.clj index 942be28..6cc6887 100644 --- a/test/sentry_clj/core_test.clj +++ b/test/sentry_clj/core_test.clj @@ -206,7 +206,14 @@ (defexpect map->event-test-with-ex-info (expecting "an ex-info event" - (let [output (serialize (assoc event :throwable (ex-info "bad stuff" {:ex-info 2}))) + (let [output (serialize (assoc event :throwable (ex-info "bad stuff" + {:data 1} + (ex-info "this is a transitive cause" + {:data 2} + (RuntimeException. + "this is a non-info transitive cause" + (ex-info "this is the root cause" + {:data 3})))))) actual (strip-timestamp output)] (expect {"release" "v1.0.0" "event_id" "4c4fbea957a74c99808d2284306e6c98" @@ -232,7 +239,9 @@ "url" "http://example.com"} "transaction" "456" "extra" {"one" {"two" 2} - "ex-info" 2} + "ex-data" {"data" 1} + "ex-data, cause 1: this is a transitive cause" {"data" 2} + "ex-data, cause 3: this is the root cause" {"data" 3}} "platform" "clojure" "contexts" {} "breadcrumbs" [{"type" "http"