Compare commits

...

2 Commits

Author SHA1 Message Date
Andrey Antukh
ca9de16755 Improve unhandled exception handling 2026-01-27 13:04:31 +01:00
Andrey Antukh
730620a432 Add helper for proper print js exceptions 2026-01-27 13:03:28 +01:00
6 changed files with 60 additions and 10 deletions

View File

@@ -241,7 +241,20 @@
(with-out-str
(print-all cause)))))
#?(:clj
(defn print-throwable
[cause & {:as opts}]
(println (format-throwable cause opts))))
(defn print-throwable
[cause & {:as opts}]
#?(:clj
(println (format-throwable cause opts))
:cljs
(let [prefix (get opts :prefix "exception")
title (str prefix ": " (ex-message cause))
exdata (ex-data cause)]
(js/console.group title)
(when-let [explain (get exdata ::sm/explain)]
(println (sm/humanize-explain explain)))
(js/console.log "\nData:")
(pp/pprint (dissoc exdata ::sm/explain))
(js/console.log "\nTrace:")
(js/console.error (.-stack cause)))))

View File

@@ -29,6 +29,9 @@
;; Will contain the latest error report assigned
(def last-report nil)
;; Will contain last uncaught exception
(def last-exception nil)
(defn- print-data!
[data]
(-> data
@@ -338,7 +341,6 @@
(print-data! werror)
(print-explain! werror))))))))
(defonce uncaught-error-handler
(letfn [(is-ignorable-exception? [cause]
(let [message (ex-message cause)]
@@ -349,10 +351,31 @@
(on-unhandled-error [event]
(.preventDefault ^js event)
(when-let [error (unchecked-get event "error")]
(when-not (is-ignorable-exception? error)
(on-error error))))]
(when-let [cause (unchecked-get event "error")]
(set! last-exception cause)
(when-not (is-ignorable-exception? cause)
(ex/print-throwable cause :prefix "uncaught exception")
(st/async-emit!
(ntf/show {:content (tr "errors.unexpected-exception" (ex-message cause))
:type :toast
:level :error
:timeout 3000})))))
(on-unhandled-rejection [event]
(.preventDefault ^js event)
(when-let [cause (unchecked-get event "reason")]
(set! last-exception cause)
(ex/print-throwable cause :prefix "uncaught rejection")
(st/async-emit!
(ntf/show {:content (tr "errors.unexpected-exception" (ex-message cause))
:type :toast
:level :error
:timeout 3000}))))]
(.addEventListener glob/window "error" on-unhandled-error)
(.addEventListener glob/window "unhandledrejection" on-unhandled-rejection)
(fn []
(.removeEventListener glob/window "error" on-unhandled-error))))
(.removeEventListener glob/window "error" on-unhandled-error)
(.removeEventListener glob/window "unhandledrejection" on-unhandled-rejection))))

View File

@@ -8,6 +8,7 @@
"React error boundary components"
(:require
["react-error-boundary" :as reb]
[app.common.exceptions :as ex]
[app.main.errors :as errors]
[app.main.refs :as refs]
[goog.functions :as gfn]
@@ -34,7 +35,8 @@
;; very small amount of time, so we debounce for 100ms for
;; avoid duplicate and redundant reports
(gfn/debounce (fn [error info]
(js/console.log "Cause stack: \n" (.-stack error))
(set! errors/last-exception error)
(ex/print-throwable error)
(js/console.error
"Component trace: \n"
(unchecked-get info "componentStack")

View File

@@ -8,6 +8,7 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.files.repair :as cfr]
[app.common.files.validate :as cfv]
[app.common.json :as json]
@@ -456,3 +457,8 @@
(defn ^:export network-averages
[]
(.log js/console (clj->js @http/network-averages)))
(defn print-last-exception
[]
(some-> errors/last-exception ex/print-throwable))

View File

@@ -1466,6 +1466,9 @@ msgstr ""
msgid "errors.generic"
msgstr "Something wrong has happened."
msgid "errors.unexpected-exception"
msgstr "Unexpected exception: %s"
#: src/app/main/errors.cljs:200
msgid "errors.internal-assertion-error"
msgstr "Internal Assertion Error"

View File

@@ -1462,6 +1462,9 @@ msgstr ""
msgid "errors.generic"
msgstr "Ha ocurrido algún error."
msgid "errors.unexpected-exception"
msgstr "Error inesperado: %s"
#: src/app/main/errors.cljs:200
msgid "errors.internal-assertion-error"
msgstr "Error interno de aserción"