diff --git a/resources/signal-docstrings/catch-to-error!.txt b/resources/signal-docstrings/catch-to-error!.txt new file mode 100644 index 0000000..e9b7ea4 --- /dev/null +++ b/resources/signal-docstrings/catch-to-error!.txt @@ -0,0 +1,30 @@ +Unconditionally executes given form and- + If form succeeds: return the form's result. + If form throws: + Call `error!` with the thrown error and the given signal options [1], + then return (:catch-val opts) if it exists, or rethrow the error. + +API: [form] [id-or-opts form] => form's result (value/throw) (unconditional), or (:catch-val opts) +Default kind: `:error` +Default level: `:error` + +Examples: + + (catch->error! (/ 1 0)) ; %> {:kind :error, :level :error, :error ...} + (catch->error! {:id ::my-id, :catch-val "threw"} (/ 1 0)) ; %> {... :id ::my-id ...} + (catch->error! + {:let [x "x"] ; Available to `:data` and `:msg` + :data {:x x} + :msg_ ["My msg:" x]} + (/ 1 0)) ; %> {... :data {x "x"}, :msg_ "My msg: x" ...} + +Tips: + + - Test using `with-signals`: (with-signals (catch->error! ...)). + - Supports the same options as other signals [1]. + + - Useful for recording errors in forms, futures, callbacks, etc. + +See also `error!`. + +[1] See `help:signal-options` docstring diff --git a/resources/signal-docstrings/error!.txt b/resources/signal-docstrings/error!.txt new file mode 100644 index 0000000..655eb16 --- /dev/null +++ b/resources/signal-docstrings/error!.txt @@ -0,0 +1,29 @@ +"Error" signal call, focused on error + id. + +API: [error] [id-or-opts error] => given error (unconditional) +Default kind: `:error` +Default level: `:error` + +Examples: + + (throw (error! (ex-info "MyEx" {}))) ; %> {:kind :error, :level :error, :error ...} + (throw (error! ::my-id (ex-info "MyEx" {}))) ; %> {... :id ::my-id ...} + (throw + (error! + {:let [x "x"] ; Available to `:data` and `:msg` + :data {:x x} + :msg ["My message:" x]} + + (ex-info "MyEx" {}))) ; %> {... :data {x "x"}, :msg_ "My msg: x" ...} + +Tips: + + - Test using `with-signals`: (with-signals (error! ...)). + - Supports the same options as other signals [3]. + + - `error` arg is a platform error (`java.lang.Throwable` or `js/Error`). + - Can conveniently be wrapped by `throw`: (throw (error! ...)). + +[1] See `help:signal-handling` docstring +[2] See `help:signal-content` docstring +[3] See `help:signal-options` docstring diff --git a/resources/signal-docstrings/event!.txt b/resources/signal-docstrings/event!.txt new file mode 100644 index 0000000..f449772 --- /dev/null +++ b/resources/signal-docstrings/event!.txt @@ -0,0 +1,34 @@ +"Event" signal call, focused on id + level. + +API: [id] [id level-or-opts] => true iff signal call was allowed +Default kind: `:event` +Default level: `:info` + +When conditions are met [1], creates a Telemere signal [2] and dispatches it to +registered handlers for processing (writing to console/disk/db, etc.). + +Examples: + + (event! ::my-id) ; %> {:kind :event, :level :info, :id ::my-id ...} + (event! ::my-id :warn) ; %> {... :level :warn ...} + (event! ::my-id + {:let [x "x"] ; Available to `:data` and `:msg` + :data {:x x} + :msg ["My msg:" x]}) ; %> {... :data {x "x"}, :msg_ "My msg: x" ...} + +Tips: + + - Test using `with-signals`: (with-signals (error! ...)). + - Supports the same options as other signals [3]. + + - A good general-purpose signal, prefer to `log!` by default, since it + better encourages structured data over unstructured messages. + + - Has a different 2-arity arg order to all other signals! + Mnemonic: the arg that's typically larger is *always* in the rightmost + position, and for `event!` that's the `level-or-opts` arg. + +---------------------------------------- +[1] See `help:signal-handling` docstring +[2] See `help:signal-content` docstring +[3] See `help:signal-options` docstring diff --git a/resources/signal-docstrings/log!.txt b/resources/signal-docstrings/log!.txt new file mode 100644 index 0000000..6a41edb --- /dev/null +++ b/resources/signal-docstrings/log!.txt @@ -0,0 +1,34 @@ +"Log" signal call, focused on message + level. + +API: [msg] [level-or-opts msg] => true iff signal call was allowed. +Default kind: `:log` +Default level: `:info` + +When conditions are met [1], creates a Telemere signal [2] and dispatches it to +registered handlers for processing (writing to console/disk/db, etc.). + +Examples: + + (log! "My msg") ; %> {:kind :log, :level :info, :id ::my-id ...} + (log! :warn "My msg") ; %> {... :level :warn ...} + (log! + {:let [x "x"] ; Available to `:data` and `:msg` + :data {:x x}} + + ["My msg:" x]) ; %> {... :data {x "x"}, :msg_ "My msg: x" ...} + +Tips: + + - Test using `with-signals`: (with-signals (log! ...)). + - Supports the same options as other signals [3]. + + - Prefer `event!` to `log!` by default, since it better encourages structured + data over unstructured messages. + + - `msg` arg may be a string, or vector of strings to join with `\space`. + - See also `msg-splice`, `msg-skip` utils. + +---------------------------------------- +[1] See `help:signal-handling` docstring +[2] See `help:signal-content` docstring +[3] See `help:signal-options` docstring diff --git a/resources/signal-docstrings/signal!.txt b/resources/signal-docstrings/signal!.txt new file mode 100644 index 0000000..6cd2cd5 --- /dev/null +++ b/resources/signal-docstrings/signal!.txt @@ -0,0 +1,35 @@ +Low-level generic signal call. + +API: [opts] => depends on options [3] +Default kind: none (optional) +Default level: none (must be provided) + +When conditions are met [1], creates a Telemere signal [2] and dispatches it to +registered handlers for processing (writing to console/disk/db, etc.). + +If `:run` option is provided: returns value of given run form, or throws. + Otherwise: returns true iff call conditions were met. + +Generic signals are fairly low-level and useful mostly for library authors or +advanced users writing their own wrapper macros. Regular users will typically +prefer one of the provided wrapper macros optimized for ease-of-use in +common cases. + +These all use `signal!` underneath and offer the same options, but vary in +their defaults and the focus of their call APIs (args and return values): + + `event!` - (id + opts/level) => true iff signal call was allowed + `log!` - (message + opts/level) => true iff signal call was allowed + `error!` - (error + opts/id) => given error (unconditional) + `trace!` - (form + opts/id) => form's result (value/throw) (unconditional) + `spy!` - (form + opts/level) => form's result (value/throw) (unconditional) + +Tips: + + - Test using `with-signals`: (with-signals (signal! ...)). + - Supports the same options as other signals [3]. + +---------------------------------------- +[1] See `help:signal-handling` docstring +[2] See `help:signal-content` docstring +[3] See `help:signal-options` docstring diff --git a/resources/signal-docstrings/signal-content.txt b/resources/signal-docstrings/signal-content.txt new file mode 100644 index 0000000..bdfeb6a --- /dev/null +++ b/resources/signal-docstrings/signal-content.txt @@ -0,0 +1,36 @@ +Signals are initially maps with {:keys [instant id ns level data msg_ ...]}, +though they can be modified by call and/or handler middleware. + +Default keys: + +`:schema-version` - Int version of signal schema (current: 1) + +`:instant` - Platform instant [1] when signal was created +`:level` - Signal level ∈ #{ :trace :debug :info :warn :error :fatal :report ...} +`:kind` - Signal ?kind ∈ #{nil :event :error :log :trace :spy ...} +`:id` - ?id of signal call (common to all signals created by signal call, contrast with `:uid`) +`:uid` - ?id of signal instance (unique to each signal created by signal call, contrast with `:id`) + +`:data` - Arb user-level data ?val (usu. a map) given to signal call +`:msg` - Arb user-level message ?str given to signal call +`:error` - Arb user-level platform ?error [2] given to signal call + +`:run-form` - Unevaluated ?form given to signal macro as `:run` +`:run-value` - Successful return ?val of `:run` ?form +`:end-instant` - Platform ?instant [1] when `:run` ?form completed +`:runtime-nsecs`- ?int nanosecs runtime of `:run` ?form + +`:ctx` - ?val of `*ctx*` (arb user-level state) when signal was created +`:parent` - ?{:keys [id uid]} of parent signal, present in nested signals when tracing +`:location` - ?{:keys [ns file line column]} signal call location +`:ns` - ?str namespace of signal call, same as (:ns location) +`:file` - ?str filename of signal call, same as (:file location) +`:line` - ?int line of signal call, same as (:line location) +`:column` - ?int column of signal call, same as (:column location) + +`:sample-rate` - ?rate ∈ℝ[0,1] for combined call AND handler sampling (0.75 => allow 75% of signals, nil => allow all) + +If anything is unclear, please ping me (@ptaoussanis) so that I can improve these docs! + +[1] Clj: `java.time.Instant`, Cljs: `js/Date` +[2] Clj: `java.lang.Throwable`, Cljs: `js/Error` diff --git a/resources/signal-docstrings/signal-handling.txt b/resources/signal-docstrings/signal-handling.txt new file mode 100644 index 0000000..37b431b --- /dev/null +++ b/resources/signal-docstrings/signal-handling.txt @@ -0,0 +1,17 @@ +A signal will be provided to a handler iff ALL of the following are true: + + 1. Signal call is allowed by compile-time filters + 2. Signal call is allowed by runtime filters + 3. Handler is allowed by runtime filters + + 4. Signal call middleware does not suppress the signal (return nil) + 5. Handler middleware does not suppress the signal (return nil) + +For more info: + + - On call filters, see: `help:filters` docstring + - On handler filters, see: `help:handlers` docstring + - On signal flow, see: Ref. + +If anything is unclear, please ping me (@ptaoussanis) so that I can +improve these docs! diff --git a/resources/signal-docstrings/signal-options.txt b/resources/signal-docstrings/signal-options.txt new file mode 100644 index 0000000..68ef23d --- /dev/null +++ b/resources/signal-docstrings/signal-options.txt @@ -0,0 +1,33 @@ +Signal options (shared by `signal!`, `event!`, ...): + +`:instant` - Platform instant [1] when signal was created, ∈ #{nil :auto } +`:level` - Signal level ∈ #{ :trace :debug :info :warn :error :fatal :report ...} +`:kind` - Signal ?kind ∈ #{nil :event :error :log :trace :spy ...} +`:id` - ?id of signal call (common to all signals created by signal call, contrast with `:uid`) +`:uid` - ?id of signal instance (unique to each signal created by signal call, contrast with `:id`) + +`:data` - Arb user-level ?data to incl. in signal: usu. a map +`:msg` - Arb user-level ?message to incl. in signal: str or vec of strs to join (with `\space`) +`:error` - Arb user-level ?error to incl. in signal: platform error [2] + +`:run` - ?form to execute UNCONDITIONALLY; will incl. `:run-value` in signal +`:do` - ?form to execute conditionally (iff signal allowed), before establishing `:let` ?binding +`:let` - ?binding to establish conditionally (iff signal allowed), BEFORE evaluating `:data` and `:msg` (useful!) + +`:ctx` - Custom ?val to override auto (dynamic `*ctx*`) in signal +`:parent` - Custom ?{:keys [id uid]} to override auto (dynamic) parent signal info in signal +`:location` - Custom ?{:keys [ns line column file]} to override auto signal call location + +`:elidable?` - Should signal call be subject to compile-time elision? (Default: true) +`:sample-rate` - ?rate ∈ℝ[0,1] for call sampling (0.75 => allow 75% of signals, nil => allow all) +`:when` - Arb ?form; when present, form must return truthy to allow signal +`:rate-limit` - ?spec as given to `telemere/rate-limiter`, see its docstring for details +`:middleware` - ?[(fn [signal])=>modified-signal ...] call middleware +`:trace?` - Should tracing be enabled for `:run` form? + + - Arb user-level ?kvs to incl. in signal + +If anything is unclear, please ping me (@ptaoussanis) so that I can improve these docs! + +[1] Clj: `java.time.Instant`, Cljs: `js/Date` +[2] Clj: `java.lang.Throwable`, Cljs: `js/Error` diff --git a/resources/signal-docstrings/spy!.txt b/resources/signal-docstrings/spy!.txt new file mode 100644 index 0000000..ad60980 --- /dev/null +++ b/resources/signal-docstrings/spy!.txt @@ -0,0 +1,36 @@ +"Spy" signal call, focused on form + level. + +API: [form] [level-or-opts form] => form's result (value/throw) (unconditional) +Default kind: `:spy` +Default level: `:info` + +When conditions are met [1], creates a Telemere signal [2] and dispatches it to +registered handlers for processing (writing to console/disk/db, etc.). + +Examples: + + (spy! (+ 1 2)) ; %> {:kind :trace, :level :info, :run-form '(+ 1 2), + ; :run-value 3, :parent {:keys [id uid]} + ; :msg "(+ 1 2) => 3" ...} + (spy! ::my-id (+ 1 2)) ; %> {... :id ::my-id ...} + (spy! + {:let [x "x"] ; Available to `:data` and `:msg` + :data {:x x}} + + (+ 1 2)) ; %> {... :data {x "x"}, :msg_ "My msg: x" ...} + +Tips: + + - Test using `with-signals`: (with-signals (spy! ...)). + - Supports the same options as other signals [3]. + + - Identical to `trace!`, but focused on form + level rather than form + id. + + - Useful for debugging/monitoring forms, and tracing (nested) execution flow. + - Execution of `form` arg may trigger additional (nested) signals. + Each signal's `:parent` key will indicate its immediate parent. + +---------------------------------------- +[1] See `help:signal-handling` docstring +[2] See `help:signal-content` docstring +[3] See `help:signal-options` docstring diff --git a/resources/signal-docstrings/trace!.txt b/resources/signal-docstrings/trace!.txt new file mode 100644 index 0000000..99cdff0 --- /dev/null +++ b/resources/signal-docstrings/trace!.txt @@ -0,0 +1,40 @@ +"Trace" signal call, focused on form + id. + +API: [form] [id-or-opts form] => form's result (value/throw) (unconditional) +Default kind: `:trace` +Default level: `:info` (intentionally NOT `:trace`!) + +When conditions are met [1], creates a Telemere signal [2] and dispatches it to +registered handlers for processing (writing to console/disk/db, etc.). + +Examples: + + (trace! (+ 1 2)) ; %> {:kind :trace, :level :info, :run-form '(+ 1 2), + ; :run-value 3, :parent {:keys [id uid]} ... + ; :msg "(+ 1 2) => 3" ...} + (trace! ::my-id (+ 1 2)) ; %> {... :id ::my-id ...} + (trace! + {:let [x "x"] ; Available to `:data` and `:msg` + :data {:x x}} + + (+ 1 2)) ; %> {... :data {x "x"}, :msg_ "My msg: x" ...} + +Tips: + + - Test using `with-signals`: (with-signals (trace! ...)). + - Supports the same options as other signals [3]. + + - Identical to `spy!`, but focused on form + id rather than form + level. + + - Useful for debugging/monitoring forms, and tracing (nested) execution flow. + - Execution of `form` arg may trigger additional (nested) signals. + Each signal's `:parent` key will indicate its immediate parent. + + - Default level is `:info`, not `:trace`! The name "trace" in "trace signal" + refers to the general action of tracing program flow rather than to the + common logging level of the same name. + +---------------------------------------- +[1] See `help:signal-handling` docstring +[2] See `help:signal-content` docstring +[3] See `help:signal-options` docstring diff --git a/src/taoensso/telemere.cljc b/src/taoensso/telemere.cljc index b498c97..be5f971 100644 --- a/src/taoensso/telemere.cljc +++ b/src/taoensso/telemere.cljc @@ -58,8 +58,8 @@ (comment [level-aliases] - [handlers-help get-handlers add-handler! remove-handler! with-handler with-handler+] - [filtering-help get-filters get-min-level + [help:handlers get-handlers add-handler! remove-handler! with-handler with-handler+] + [help:filtering get-filters get-min-level set-kind-filter! set-ns-filter! set-id-filter! set-min-level! with-kind-filter with-ns-filter with-id-filter with-min-level]) @@ -78,6 +78,14 @@ #?(:clj impl/with-signals) #?(:clj impl/signal!)) +;;;; Signal help + +(comment help:filters help:handlers) ; Via Encore + +(impl/defhelp help:signal-handling :signal-handling) +(impl/defhelp help:signal-content :signal-content) +(impl/defhelp help:signal-options :signal-options) + ;;;; Context (enc/defonce default-ctx @@ -88,7 +96,7 @@ (enc/get-env {:as :edn} :taoensso.telemere/default-ctx<.platform><.edn>)) (enc/def* ^:dynamic *ctx* - "Dynamic context: arbitrary app-level state attached as `:ctx` to all signals. + "Dynamic context: arbitrary user-level state attached as `:ctx` to all signals. Value may be any type, but is usually nil or a map. Re/bind dynamic value using `with-ctx`, `with-ctx+`, or `binding`. @@ -179,68 +187,120 @@ :data data})))) ;;;; Common signals -;; - log! [msg] [level-or-opts msg] ; msg + ?level => allowed? -;; - event! [id] [level-or-opts id] ; id + ?level => allowed? -;; - error! [error] [id-or-opts error] ; error + ?id => error -;; - trace! [form] [id-or-opts form] ; run + ?id => run result (value or throw) -;; - spy! [form] [level-or-opts form] ; run + ?level => run result (value or throw) -;; - catch->error! [form] [id-or-opts form] ; run + ?id => run value or ?return -;; - uncaught->error! [] [id-or-opts ] ; ?id => nil - -#?(:clj - (defmacro log! - "TODO Docstring [msg] [level-or-opts msg] => allowed?" - {:arglists (impl/signal-arglists :log!)} - [& args] - (let [opts (apply impl/signal-opts :msg, :level, {:kind :log, :level :info} args)] - (enc/keep-callsite `(impl/signal! ~opts))))) +;; - signal! [ opts] ; => allowed? / run result (value or throw) +;; - event! [id ] [id level-or-opts] ; id + ?level => allowed? ; Sole signal with descending main arg! +;; - log! [msg ] [level-or-opts msg] ; msg + ?level => allowed? +;; - error! [error] [id-or-opts error] ; error + ?id => given error +;; - trace! [form ] [id-or-opts form] ; run + ?id => run result (value or throw) +;; - spy! [form ] [level-or-opts form] ; run + ?level => run result (value or throw) +;; - catch->error! [form ] [id-or-opts form] ; run + ?id => run value or ?return +;; - uncaught->error! [ ] [id-or-opts ] ; ?id => nil #?(:clj (defmacro event! - "TODO Docstring [id] [level-or-opts id] => allowed?" - {:arglists (impl/signal-arglists :event!)} + "[id] [id level-or-opts] => allowed?" + {:doc (impl/signal-docstring :event!) + :arglists (impl/signal-arglists :event!)} [& args] - (let [opts (apply impl/signal-opts :id, :level, {:kind :event, :level :info} args)] + (let [opts (impl/signal-opts `event! {:kind :event, :level :info} :id :level :dsc args)] (enc/keep-callsite `(impl/signal! ~opts))))) +(comment + (with-signal (event! ::my-id)) + (with-signal (event! ::my-id :warn)) + (with-signal + (event! ::my-id + {:let [x "x"] ; Available to `:data` and `:msg` + :data {:x x} + :msg ["My msg:" x]}))) + +#?(:clj + (defmacro log! + "[msg] [level-or-opts msg] => allowed?" + {:doc (impl/signal-docstring :log!) + :arglists (impl/signal-arglists :log!)} + [& args] + (let [opts (impl/signal-opts `log! {:kind :log, :level :info} :msg :level :asc args)] + (enc/keep-callsite `(impl/signal! ~opts))))) + +(comment + (with-signal (log! "My msg")) + (with-signal (log! :warn "My msg")) + (with-signal + (log! + {:let [x "x"] ; Available to `:data` and `:msg` + :data {:x x}} + ["My msg:" x]))) + #?(:clj (defmacro error! - "TODO Docstring [error] [id-or-opts error] => error - (throw (error! )) example." - {:arglists (impl/signal-arglists :error!)} + "[error] [error id-or-opts] => error" + {:doc (impl/signal-docstring :error!) + :arglists (impl/signal-arglists :error!)} [& args] - (let [opts (apply impl/signal-opts :error, :id, {:kind :error, :level :error} args) + (let [opts (impl/signal-opts `error! {:kind :error, :level :error} :error :id :asc args) error-form (get opts :error)] - ;; (enc/keep-callsite `(impl/signal! ~opts)) ; => allowed? + (enc/keep-callsite `(let [~'__error ~error-form] (impl/signal! ~(assoc opts :error '__error)) - ~'__error))))) + ~'__error ; Unconditional! + ))))) -(comment (throw (error! (Exception. "hello")))) +(comment + (with-signal (throw (error! (ex-info "MyEx" {})))) + (with-signal (throw (error! ::my-id (ex-info "MyEx" {})))) + (with-signal + (throw + (error! + {:let [x "x"] ; Available to `:data` and `:msg` + :data {:x x} + :msg ["My msg:" x]} + (ex-info "MyEx" {}))))) #?(:clj (defmacro trace! - "TODO Docstring [form] [id-or-opts form] => run result (value or throw)" - {:arglists (impl/signal-arglists :trace!)} + "[form] [id-or-opts form] => run result (value or throw)" + {:doc (impl/signal-docstring :trace!) + :arglists (impl/signal-arglists :trace!)} [& args] - (let [opts (apply impl/signal-opts :run, :id, {:kind :trace, :level :info} args)] + (let [opts (impl/signal-opts `trace! {:kind :trace, :level :info, :msg ::impl/spy} :run :id :asc args)] (enc/keep-callsite `(impl/signal! ~opts))))) +(comment + (with-signal (trace! (+ 1 2))) + (with-signal (trace! ::my-id (+ 1 2))) + (with-signal + (trace! + {:let [x "x"] ; Available to `:data` and `:msg` + :data {:x x}} + (+ 1 2)))) + #?(:clj (defmacro spy! - "TODO Docstring [form] [level-or-opts form] => run result (value or throw)" - {:arglists (impl/signal-arglists :spy!)} + "[form] [level-or-opts form] => run result (value or throw)" + {:doc (impl/signal-docstring :spy!) + :arglists (impl/signal-arglists :spy!)} [& args] - (let [opts (apply impl/signal-opts :run, :level, {:kind :spy, :level :info, :msg ::impl/spy} args)] + (let [opts (impl/signal-opts `spy! {:kind :spy, :level :info, :msg ::impl/spy} :run :level :asc args)] (enc/keep-callsite `(impl/signal! ~opts))))) +(comment + (with-signal (spy! (+ 1 2))) + (with-signal (spy! ::my-id (+ 1 2))) + (with-signal + (spy! + {:let [x "x"] ; Available to `:data` and `:msg` + :data {:x x}} + (+ 1 2)))) + #?(:clj (defmacro catch->error! - "TODO Docstring [form] [id-or-opts form] => run value or ?catch-val" - {:arglists (impl/signal-arglists :catch->error!)} + "[form] [id-or-opts form] => run value or ?catch-val" + {:doc (impl/signal-docstring :catch-to-error!) + :arglists (impl/signal-arglists :catch->error!)} [& args] - (let [opts (apply impl/signal-opts ::__form, :id, {:kind :error, :level :error} args) + (let [opts (impl/signal-opts `catch->error! {:kind :error, :level :error} ::__form :id :asc args) rethrow? (if (contains? opts :catch-val) false (get opts :rethrow?)) catch-val (get opts :catch-val) form (get opts ::__form) @@ -248,26 +308,38 @@ (enc/keep-callsite `(enc/try* ~form - (catch :any ~'__t - (impl/signal! ~(assoc opts :error '__t)) - (if ~rethrow? (throw ~'__t) ~catch-val))))))) + (catch :any ~'__caught-error + (impl/signal! ~(assoc opts :error '__caught-error)) + (if ~rethrow? (throw ~'__caught-error) ~catch-val))))))) -(comment (catch->error! {:id :id1, :catch-val "threw"} (/ 1 0))) +(comment + (with-signal (catch->error! (/ 1 0))) + (with-signal (catch->error! {:id ::my-id, :catch-val "threw"} (/ 1 0))) + (with-signal + (catch->error! + {:let [x "x"] ; Available to `:data` and `:msg` + :data {:x x} + :msg_ ["My msg:" x __caught-error]} + (/ 1 0)))) #?(:clj (defmacro uncaught->error! - "TODO Docstring - See also `uncaught->handler!`." - {:arglists (impl/signal-arglists :uncaught->error!)} - ([ ] (enc/keep-callsite `(uncaught->error! nil))) - ([id-or-opts] - (let [msg-form ["Uncaught Throwable on thread: " `(.getName ~(with-meta '__thread {:tag 'java.lang.Thread}))] - opts (impl/signal-opts :error, :id, {:kind :error, :level :error, :msg msg-form} id-or-opts '__throwable)] + "Uses `uncaught->handler!` so that `error!` will be called for + uncaught JVM errors. - (enc/keep-callsite - `(uncaught->handler! - (fn [~'__thread ~'__throwable] - (impl/signal! ~opts)))))))) + See `uncaught->handler!` and `error!` for details." + {:arglists (impl/signal-arglists :uncaught->error!)} + [& args] + (let [msg-form ["Uncaught Throwable on thread: " `(.getName ~(with-meta '__thread {:tag 'java.lang.Thread}))] + opts + (impl/signal-opts `uncaught->error! + {:kind :error, :level :error, :msg msg-form} + :error :id :dsc (into ['__throwable] args))] + + (enc/keep-callsite + `(uncaught->handler! + (fn [~'__thread ~'__throwable] + (impl/signal! ~opts))))))) (comment (macroexpand '(uncaught->error! :id1))) @@ -277,12 +349,14 @@ (defn uncaught->handler! "Sets JVM's global `DefaultUncaughtExceptionHandler` to given (fn handler [`` ``]). + See also `uncaught->error!`." [handler] (Thread/setDefaultUncaughtExceptionHandler (reify Thread$UncaughtExceptionHandler (uncaughtException [_ thread throwable] - (handler thread throwable)))))) + (handler thread throwable)))) + nil)) #?(:clj (defn hostname diff --git a/src/taoensso/telemere/impl.cljc b/src/taoensso/telemere/impl.cljc index b7f7591..6bdeb5d 100644 --- a/src/taoensso/telemere/impl.cljc +++ b/src/taoensso/telemere/impl.cljc @@ -194,7 +194,7 @@ (defrecord Signal ;; Telemere's main public data type, we avoid nesting and duplication [^long schema-version instant uid, - callsite-id location ns line column file, + location ns line column file, sample-rate, kind id level, ctx parent, data msg_ error run-form run-value, end-instant runtime-nsecs]) @@ -256,7 +256,7 @@ #?(:clj (defmacro ^:public with-signals "Executes given form and records any signals triggered by it. - Return value depends on options. Useful for tests/debugging. + Return value depends on given options. Useful for tests/debugging. Options: @@ -301,7 +301,7 @@ ^Signal ;; Note all dynamic vals passed as explicit args for better control [instant uid, - callsite-id location ns line column file, + location ns line column file, sample-rate, kind id level, ctx parent, user-opts data msg_, run-form run-result error] @@ -321,14 +321,14 @@ msg_)] (Signal. 1 instant uid, - callsite-id location ns line column file, + location ns line column file, sample-rate, kind id level, ctx parent, data msg_, run-error run-form run-value, end-instant runtime-nsecs)) (Signal. 1 instant uid, - callsite-id location ns line column file, + location ns line column file, sample-rate, kind id level, ctx parent, data msg_, error nil nil instant nil))] @@ -340,66 +340,69 @@ (enc/qb 1e6 ; 55.67 (new-signal nil nil nil nil nil nil nil nil nil nil - nil nil nil nil nil nil nil nil nil nil))) + nil nil nil nil nil nil nil nil nil))) ;;;; Signal API helpers +#?(:clj (defmacro signal-docstring [rname] (enc/slurp-resource (str "signal-docstrings/" (name rname) ".txt")))) +#?(:clj (defmacro defhelp [sym rname] `(enc/def* ~sym {:doc ~(eval `(signal-docstring ~rname))} "See docstring"))) + #?(:clj (defn signal-arglists [macro-id] (case macro-id - :signal! ; [opts] => or + :signal! ; [opts] => allowed? / run result (value or throw) '([{:as opts :keys - [#_defaults #_elide? #_allow? #_callsite-id, + [#_defaults #_elide? #_allow? #_expansion-id, ; Undocumented elidable? location instant uid middleware, sample-rate ns kind id level when rate-limit, ctx parent trace?, do let data msg error run & user-opts]}]) - :log! ; [msg] [level-or-opts msg] => + :event! ; [id] [id level-or-opts] => allowed? + '([id ] + [id level] + [id + {:as opts :keys + [#_defaults #_elide? #_allow? #_expansion-id, + elidable? location instant uid middleware, + sample-rate ns kind id level when rate-limit, + ctx parent trace?, do let data msg error #_run & user-opts]}]) + + :log! ; [msg] [level-or-opts msg] => allowed? '([ msg] [level msg] [{:as opts :keys - [#_defaults #_elide? #_allow? #_callsite-id, + [#_defaults #_elide? #_allow? #_expansion-id, elidable? location instant uid middleware, sample-rate ns kind id level when rate-limit, ctx parent trace?, do let data msg error #_run & user-opts]} msg]) - :event! ; [id] [level-or-opts id] => - '([ id] - [level id] - [{:as opts :keys - [#_defaults #_elide? #_allow? #_callsite-id, - elidable? location instant uid middleware, - sample-rate ns kind id level when rate-limit, - ctx parent trace?, do let data msg error #_run & user-opts]} - id]) - - :error! ; [error] [id-or-opts error] => + :error! ; [error] [id-or-opts error] => given error '([ error] [id error] [{:as opts :keys - [#_defaults #_elide? #_allow? #_callsite-id, + [#_defaults #_elide? #_allow? #_expansion-id, elidable? location instant uid middleware, sample-rate ns kind id level when rate-limit, ctx parent trace?, do let data msg error #_run & user-opts]} error]) - (:trace! :spy!) ; [form] [id-or-opts form] => (value or throw) + (:trace! :spy!) ; [form] [id-or-opts form] => run result (value or throw) '([ form] [id form] [{:as opts :keys - [#_defaults #_elide? #_allow? #_callsite-id, + [#_defaults #_elide? #_allow? #_expansion-id, elidable? location instant uid middleware, sample-rate ns kind id level when rate-limit, ctx parent trace?, do let data msg error run & user-opts]} form]) - :catch->error! ; [form] [level-or-opts form] => (value or throw) - '([ form] - [level form] + :catch->error! ; [form] [id-or-opts form] => run result (value or throw) + '([ form] + [id form] [{:as opts :keys - [#_defaults #_elide? #_allow? #_callsite-id, rethrow? catch-val, + [#_defaults #_elide? #_allow? #_expansion-id, rethrow? catch-val, elidable? location instant uid middleware, sample-rate ns kind id level when rate-limit, ctx parent trace?, do let data msg error #_run & user-opts]} @@ -409,7 +412,7 @@ '([ ] [id] [{:as opts :keys - [#_defaults #_elide? #_allow? #_callsite-id, + [#_defaults #_elide? #_allow? #_expansion-id, elidable? location instant uid middleware, sample-rate ns kind id level when rate-limit, ctx parent trace?, do let data msg error #_run & user-opts]}]) @@ -418,48 +421,46 @@ #?(:clj (defn signal-opts - "Util to help write common signal wrapper macros: - [[] val-y] => signal-opts - [[] opts-or-val-x val-y] => signal-opts" - ([arg-key or-key defaults arg-val] {:defaults defaults, arg-key arg-val}) - ([arg-key or-key defaults opts-or-key arg-val] - (if (map? opts-or-key) - (let [opts opts-or-key] (conj {:defaults defaults, arg-key arg-val} opts)) - (let [or-val opts-or-key] {:defaults defaults, arg-key arg-val, or-key or-val}))))) + "Util to help write common signal wrapper macros." + [context defaults main-key extra-key arg-order args] -(comment - [(signal-opts :msg :level {:level :info} "foo") - (signal-opts :msg :level {:level :info} {:level :warn} "foo") - (signal-opts :msg :level {:level :info} :warn "foo")]) + (enc/cond + :let [num-args (count args)] + + (not (#{1 2} num-args)) + (throw + (ex-info (str "Wrong number of args (" num-args ") passed to: " context) + {:context context + :num-args {:actual num-args, :expected #{1 2}} + :args args})) + + :let [[main-val extra-val-or-opts] + (case arg-order + :dsc args, ; [main ...] + :asc (reverse args) ; [... main] + (enc/unexpected-arg! + arg-order))] + + (if (or (= num-args 1) (map? extra-val-or-opts)) + (let [opts extra-val-or-opts] (conj {:defaults defaults, main-key main-val } opts)) + (let [extra-val extra-val-or-opts] {:defaults defaults, main-key main-val, extra-key extra-val}))))) + +(comment (signal-opts `signal! {:level :info} :id :level :dsc [::my-id {:level :warn}])) ;;;; Signal macro #?(:clj (defmacro ^:public signal! - "Expands to a low-level signal call. - - TODO Docstring - - How low-level is this? Should location, ctx, etc. be in public arglists? - - Describe - - Reference diagram link [1] - - Mention ability to delay-wrap :data - - Mention combo `:sample-rate` stuff (call * handler) - - - Document Signal fields - - Link to signal-flow diagram - - - If :run => returns body run-result (re-throwing) - Otherwise returns true iff call allowed - - [1] Ref. " - {:arglists (signal-arglists :signal!)} + "Generic low-level signal call, also aliased in Encore." + {:doc (signal-docstring :signal!) + :arglists (signal-arglists :signal!)} [opts] (have? map? opts) ; We require const map keys, but vals may require eval (let [defaults (get opts :defaults) opts (merge defaults (dissoc opts :defaults)) {run-form :run} opts - {:keys [callsite-id location elide? allow?]} + {:keys [#_expansion-id location elide? allow?]} (sigs/filterable-expansion {:macro-form &form :macro-env &env @@ -510,14 +511,14 @@ :elidable? :location :instant :uid :middleware, :sample-rate :ns :kind :id :level :filter :when #_:rate-limit, :ctx :parent #_:trace?, :do :let :data :msg :error :run - :elide? :allow? :callsite-id))] + :elide? :allow? #_:expansion-id))] ;; Eval let bindings AFTER call filtering but BEFORE data, msg `(do ~do-form (let ~let-form ; Allow to throw during `signal-value_` deref (new-signal ~'__instant ~'__uid - ~callsite-id ~location ~ns ~line ~column ~file, + ~location ~ns ~line ~column ~file, ~sample-rate-form, ~kind-form ~'__id ~level-form, ~ctx-form ~parent-form, ~user-opts-form ~data-form ~msg-form, '~run-form ~'__run-result ~error-form))))] @@ -572,7 +573,7 @@ "Used only for interop (SLF4J, `clojure.tools.logging`, etc.)." {:arglists (signal-arglists :signal!)} [opts] - (let [{:keys [#_callsite-id #_location elide? allow?]} + (let [{:keys [#_expansion-id #_location elide? allow?]} (sigs/filterable-expansion {:macro-form &form :macro-env &env diff --git a/test/taoensso/telemere_tests.cljc b/test/taoensso/telemere_tests.cljc index 05b2123..db7f8b0 100644 --- a/test/taoensso/telemere_tests.cljc +++ b/test/taoensso/telemere_tests.cljc @@ -10,7 +10,7 @@ #?(:cljs (:require-macros - [taoensso.telemere-tests :refer [sig! ws ws! ws1]]))) + [taoensso.telemere-tests :refer [sig! ws wsf wst ws1]]))) (comment (remove-ns 'taoensso.telemere-tests) @@ -25,12 +25,14 @@ #?(:clj (do (defmacro ws [form] `(impl/-with-signals (fn [] ~form) {})) - (defmacro ws! [form] `(impl/-with-signals (fn [] ~form) {:trap-errors? true})) + (defmacro wsf [form] `(impl/-with-signals (fn [] ~form) {:force-msg? true})) + (defmacro wst [form] `(impl/-with-signals (fn [] ~form) {:trap-errors? true})) (defmacro ws1 [form] `(let [[_# [s1#]] (impl/-with-signals (fn [] ~form) {:force-msg? true})] s1#)))) (do (def ex1 (ex-info "TestEx" {})) - (def ex1-pred (enc/pred #(= % ex1)))) + (def ex1-pred (enc/pred #(= % ex1))) + (defn ex1! [] (throw ex1))) (def ^:dynamic *dynamic-var* nil) @@ -59,8 +61,8 @@ (is (= (ws (sig! {:level :info, :allow? false })) [nil nil]) "With runtime suppression") (is (= (ws (sig! {:level :info, :allow? false, :run (+ 1 2)})) [3 nil]) "With runtime suppression, run-form") - (is (->> (sig! {:level :info, :elide? true, :run (throw ex1)}) (throws? :ex-info "TestEx")) "With compile-time elision, throwing run-form") - (is (->> (sig! {:level :info, :allow? false, :run (throw ex1)}) (throws? :ex-info "TestEx")) "With runtime suppression, throwing run-form") + (is (->> (sig! {:level :info, :elide? true, :run (ex1!)}) (throws? :ex-info "TestEx")) "With compile-time elision, throwing run-form") + (is (->> (sig! {:level :info, :allow? false, :run (ex1!)}) (throws? :ex-info "TestEx")) "With runtime suppression, throwing run-form") (let [[rv1 [sv1]] (ws (sig! {:level :info })) [rv2 [sv2]] (ws (sig! {:level :info, :run (+ 1 2)}))] @@ -281,25 +283,25 @@ (tel/with-handler :hid1 (fn [sv] (force (:data sv)) (reset! sv_ sv)) {:async nil, :error-fn (fn [x] (reset! error_ x)), :rl-error nil, - :middleware [(fn [sv] (if *throwing-handler-middleware?* (throw ex1) sv))]} + :middleware [(fn [sv] (if *throwing-handler-middleware?* (ex1!) sv))]} - [(is (->> (sig! {:level :info, :when (throw ex1)}) (throws? :ex-info "TestEx")) "`~filterable-expansion/allow` throws at call") - (is (->> (sig! {:level :info, :instant (throw ex1)}) (throws? :ex-info "TestEx")) "`~instant-form` throws at call") - (is (->> (sig! {:level :info, :id (throw ex1)}) (throws? :ex-info "TestEx")) "`~id-form` throws at call") - (is (->> (sig! {:level :info, :uid (throw ex1)}) (throws? :ex-info "TestEx")) "`~uid-form` throws at call") - (is (->> (sig! {:level :info, :run (throw ex1)}) (throws? :ex-info "TestEx")) "`~run-form` rethrows at call") + [(is (->> (sig! {:level :info, :when (ex1!)}) (throws? :ex-info "TestEx")) "`~filterable-expansion/allow` throws at call") + (is (->> (sig! {:level :info, :instant (ex1!)}) (throws? :ex-info "TestEx")) "`~instant-form` throws at call") + (is (->> (sig! {:level :info, :id (ex1!)}) (throws? :ex-info "TestEx")) "`~id-form` throws at call") + (is (->> (sig! {:level :info, :uid (ex1!)}) (throws? :ex-info "TestEx")) "`~uid-form` throws at call") + (is (->> (sig! {:level :info, :run (ex1!)}) (throws? :ex-info "TestEx")) "`~run-form` rethrows at call") (is (sm? @sv_ {:level :info, :error ex1-pred}) "`~run-form` rethrows at call *after* dispatch") (testing "`@signal-value_`: trap with wrapped handler" [(testing "Throwing `~let-form`" (reset-state!) - [(is (true? (sig! {:level :info, :let [_ (throw ex1)]}))) + [(is (true? (sig! {:level :info, :let [_ (ex1!)]}))) (is (= @sv_ :nx)) (is (sm? @error_ {:handler-id :hid1, :error ex1-pred}))]) (testing "Throwing call middleware" (reset-state!) - [(is (true? (sig! {:level :info, :middleware [(fn [_] (throw ex1))]}))) + [(is (true? (sig! {:level :info, :middleware [(fn [_] (ex1!))]}))) (is (= @sv_ :nx)) (is (sm? @error_ {:handler-id :hid1, :error ex1-pred}))]) @@ -312,13 +314,13 @@ (testing "Throwing `@data_`" (reset-state!) - [(is (true? (sig! {:level :info, :data (delay (throw ex1))}))) + [(is (true? (sig! {:level :info, :data (delay (ex1!))}))) (is (= @sv_ :nx)) (is (sm? @error_ {:handler-id :hid1, :error ex1-pred}))]) (testing "Throwing user opt" (reset-state!) - [(is (true? (sig! {:level :info, :my-opt (throw ex1)}))) + [(is (true? (sig! {:level :info, :my-opt (ex1!)}))) (is (= @sv_ :nx)) (is (sm? @error_ {:handler-id :hid1, :error ex1-pred}))])])]))) @@ -400,21 +402,19 @@ (deftest _common-signals [#?(:clj (testing "signal-opts" - [(is (= (impl/signal-opts :msg, :level, {:level :info} "msg") {:defaults {:level :info}, :msg "msg"})) - (is (= (impl/signal-opts :msg, :level, {:level :info} {:level :warn} "msg") {:defaults {:level :info}, :msg "msg", :level :warn})) - (is (= (impl/signal-opts :msg, :level, {:level :info} :warn "msg") {:defaults {:level :info}, :msg "msg", :level :warn}))])) + [(is (= (impl/signal-opts `signal! {:level :info} :id :level :dsc [::my-id ]) {:defaults {:level :info}, :id ::my-id})) + (is (= (impl/signal-opts `signal! {:level :info} :id :level :dsc [::my-id :warn ]) {:defaults {:level :info}, :id ::my-id, :level :warn})) + (is (= (impl/signal-opts `signal! {:level :info} :id :level :dsc [::my-id {:level :warn}]) {:defaults {:level :info}, :id ::my-id, :level :warn})) - (testing "log!" ; msg + ?level => allowed? - [(let [[rv [sv]] (ws (tel/log! "msg"))] [(is (= rv true)) (is (sm? sv {:kind :log, :line :submap/ex, :msg_ "msg", :level :info}))]) - (let [[rv [sv]] (ws (tel/log! :warn "msg"))] [(is (= rv true)) (is (sm? sv {:kind :log, :line :submap/ex, :msg_ "msg", :level :warn}))]) - (let [[rv [sv]] (ws (tel/log! {:level :warn} "msg"))] [(is (= rv true)) (is (sm? sv {:kind :log, :line :submap/ex, :msg_ "msg", :level :warn}))]) - (let [[rv [sv]] (ws (tel/log! {:allow? false} "msg"))] [(is (= rv nil)) (is (nil? sv))])]) + (is (= (impl/signal-opts `signal! {:level :info} :id :level :asc [ ::my-id]) {:defaults {:level :info}, :id ::my-id})) + (is (= (impl/signal-opts `signal! {:level :info} :id :level :asc [:warn ::my-id]) {:defaults {:level :info}, :id ::my-id, :level :warn})) + (is (= (impl/signal-opts `signal! {:level :info} :id :level :asc [{:level :warn} ::my-id]) {:defaults {:level :info}, :id ::my-id, :level :warn}))])) (testing "event!" ; id + ?level => allowed? - [(let [[rv [sv]] (ws (tel/event! :id1))] [(is (= rv true)) (is (sm? sv {:kind :event, :line :submap/ex, :level :info, :id :id1}))]) - (let [[rv [sv]] (ws (tel/event! :warn :id1))] [(is (= rv true)) (is (sm? sv {:kind :event, :line :submap/ex, :level :warn, :id :id1}))]) - (let [[rv [sv]] (ws (tel/event! {:level :warn} :id1))] [(is (= rv true)) (is (sm? sv {:kind :event, :line :submap/ex, :level :warn, :id :id1}))]) - (let [[rv [sv]] (ws (tel/event! {:allow? false} :id1))] [(is (= rv nil)) (is (nil? sv))])]) + [(let [[rv [sv]] (ws (tel/event! :id1 ))] [(is (= rv true)) (is (sm? sv {:kind :event, :line :submap/ex, :level :info, :id :id1}))]) + (let [[rv [sv]] (ws (tel/event! :id1 :warn ))] [(is (= rv true)) (is (sm? sv {:kind :event, :line :submap/ex, :level :warn, :id :id1}))]) + (let [[rv [sv]] (ws (tel/event! :id1 {:level :warn}))] [(is (= rv true)) (is (sm? sv {:kind :event, :line :submap/ex, :level :warn, :id :id1}))]) + (let [[rv [sv]] (ws (tel/event! :id1 {:allow? false}))] [(is (= rv nil)) (is (nil? sv))])]) (testing "error!" ; error + ?id => error [(let [[rv [sv]] (ws (tel/error! ex1))] [(is (= rv ex1)) (is (sm? sv {:kind :error, :line :submap/ex, :level :error, :error ex1-pred, :id nil}))]) @@ -422,30 +422,38 @@ (let [[rv [sv]] (ws (tel/error! {:id :id1} ex1))] [(is (= rv ex1)) (is (sm? sv {:kind :error, :line :submap/ex, :level :error, :error ex1-pred, :id :id1}))]) (let [[rv [sv]] (ws (tel/error! {:allow? false} ex1))] [(is (= rv ex1)) (is (nil? sv))])]) + (testing "log!" ; msg + ?level => allowed? + [(let [[rv [sv]] (ws (tel/log! "msg"))] [(is (= rv true)) (is (sm? sv {:kind :log, :line :submap/ex, :msg_ "msg", :level :info}))]) + (let [[rv [sv]] (ws (tel/log! :warn "msg"))] [(is (= rv true)) (is (sm? sv {:kind :log, :line :submap/ex, :msg_ "msg", :level :warn}))]) + (let [[rv [sv]] (ws (tel/log! {:level :warn} "msg"))] [(is (= rv true)) (is (sm? sv {:kind :log, :line :submap/ex, :msg_ "msg", :level :warn}))]) + (let [[rv [sv]] (ws (tel/log! {:allow? false} "msg"))] [(is (= rv nil)) (is (nil? sv))])]) + (testing "trace!" ; run + ?id => run result (value or throw) - [(let [[rv [sv]] (ws (tel/trace! (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :trace, :line :submap/ex, :level :info, :id nil}))]) - (let [[rv [sv]] (ws (tel/trace! :id1 (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :trace, :line :submap/ex, :level :info, :id :id1}))]) - (let [[rv [sv]] (ws (tel/trace! {:id :id1} (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :trace, :line :submap/ex, :level :info, :id :id1}))]) - (let [[[_ re] [sv]] (ws! (tel/trace! :id1 (throw ex1)))] [(is (= re ex1)) (is (sm? sv {:kind :trace, :line :submap/ex, :level :info, :id :id1, :error ex1-pred}))]) - (let [[rv [sv]] (ws (tel/trace! {:allow? false} (+ 1 2)))] [(is (= rv 3)) (is (nil? sv))])]) + [(let [[rv [sv]] (wsf (tel/trace! (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :trace, :line :submap/ex, :level :info, :id nil, :msg_ "(+ 1 2) => 3"}))]) + (let [[rv [sv]] (ws (tel/trace! {:msg nil} (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :trace, :line :submap/ex, :level :info, :id nil, :msg_ nil}))]) + (let [[rv [sv]] (ws (tel/trace! :id1 (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :trace, :line :submap/ex, :level :info, :id :id1}))]) + (let [[rv [sv]] (ws (tel/trace! {:id :id1} (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :trace, :line :submap/ex, :level :info, :id :id1}))]) + (let [[[_ re] [sv]] (wst (tel/trace! :id1 (ex1!)))] [(is (= re ex1)) (is (sm? sv {:kind :trace, :line :submap/ex, :level :info, :id :id1, :error ex1-pred}))]) + (let [[rv [sv]] (ws (tel/trace! {:allow? false} (+ 1 2)))] [(is (= rv 3)) (is (nil? sv))])]) (testing "spy" ; run + ?level => run result (value or throw) - [(let [[rv [sv]] (ws (tel/spy! (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :spy, :line :submap/ex, :level :info}))]) - (let [[rv [sv]] (ws (tel/spy! :warn (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :spy, :line :submap/ex, :level :warn}))]) - (let [[rv [sv]] (ws (tel/spy! {:level :warn} (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :spy, :line :submap/ex, :level :warn}))]) - (let [[[_ re] [sv]] (ws! (tel/spy! :warn (throw ex1)))] [(is (= re ex1)) (is (sm? sv {:kind :spy, :line :submap/ex, :level :warn, :error ex1-pred}))]) - (let [[rv [sv]] (ws (tel/spy! {:allow? false} (+ 1 2)))] [(is (= rv 3)) (is (nil? sv))])]) + [(let [[rv [sv]] (wsf (tel/spy! (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :spy, :line :submap/ex, :level :info, :msg_ "(+ 1 2) => 3"}))]) + (let [[rv [sv]] (wsf (tel/spy! {:msg nil} (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :spy, :line :submap/ex, :level :info, :msg_ nil}))]) + (let [[rv [sv]] (ws (tel/spy! :warn (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :spy, :line :submap/ex, :level :warn}))]) + (let [[rv [sv]] (ws (tel/spy! {:level :warn} (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :spy, :line :submap/ex, :level :warn}))]) + (let [[[_ re] [sv]] (wst (tel/spy! :warn (ex1!)))] [(is (= re ex1)) (is (sm? sv {:kind :spy, :line :submap/ex, :level :warn, :error ex1-pred}))]) + (let [[rv [sv]] (ws (tel/spy! {:allow? false} (+ 1 2)))] [(is (= rv 3)) (is (nil? sv))])]) (testing "catch->error!" ; form + ?id => run value or ?return - [(let [[rv [sv]] (ws (tel/catch->error! (+ 1 2)))] [(is (= rv 3)) (is (nil? sv))]) - (let [[rv [sv]] (ws (tel/catch->error! (throw ex1)))] [(is (= rv nil)) (is (sm? sv {:kind :error, :line :submap/ex, :level :error, :error ex1-pred, :id nil}))]) - (let [[rv [sv]] (ws (tel/catch->error! :id1 (throw ex1)))] [(is (= rv nil)) (is (sm? sv {:kind :error, :line :submap/ex, :level :error, :error ex1-pred, :id :id1}))]) - (let [[rv [sv]] (ws (tel/catch->error! {:id :id1} (throw ex1)))] [(is (= rv nil)) (is (sm? sv {:kind :error, :line :submap/ex, :level :error, :error ex1-pred, :id :id1}))]) - (let [[[_ re] [sv]] (ws! (tel/catch->error! {:rethrow? true} (throw ex1)))] [(is (= re ex1)) (is (sm? sv {:kind :error, :line :submap/ex, :level :error, :error ex1-pred, :id nil}))]) - (let [[rv [sv]] (ws (tel/catch->error! {:catch-val :foo} (throw ex1)))] [(is (= rv :foo)) (is (sm? sv {:kind :error, :line :submap/ex, :level :error, :error ex1-pred, :id nil}))]) - (let [[rv [sv]] (ws (tel/catch->error! {:catch-val :foo} (+ 1 2)))] [(is (= rv 3)) (is (nil? sv))]) + [(let [[rv [sv]] (ws (tel/catch->error! (+ 1 2)))] [(is (= rv 3)) (is (nil? sv))]) + (let [[rv [sv]] (ws (tel/catch->error! (ex1!)))] [(is (= rv nil)) (is (sm? sv {:kind :error, :line :submap/ex, :level :error, :error ex1-pred, :id nil}))]) + (let [[rv [sv]] (ws (tel/catch->error! :id1 (ex1!)))] [(is (= rv nil)) (is (sm? sv {:kind :error, :line :submap/ex, :level :error, :error ex1-pred, :id :id1}))]) + (let [[rv [sv]] (ws (tel/catch->error! {:id :id1} (ex1!)))] [(is (= rv nil)) (is (sm? sv {:kind :error, :line :submap/ex, :level :error, :error ex1-pred, :id :id1}))]) + (let [[[_ re] [sv]] (wst (tel/catch->error! {:rethrow? true} (ex1!)))] [(is (= re ex1)) (is (sm? sv {:kind :error, :line :submap/ex, :level :error, :error ex1-pred, :id nil}))]) + (let [[rv [sv]] (ws (tel/catch->error! {:catch-val :foo} (ex1!)))] [(is (= rv :foo)) (is (sm? sv {:kind :error, :line :submap/ex, :level :error, :error ex1-pred, :id nil}))]) + (let [[rv [sv]] (ws (tel/catch->error! {:catch-val :foo} (+ 1 2)))] [(is (= rv 3)) (is (nil? sv))]) (let [[rv [sv]] (ws (tel/catch->error! {:catch-val :foo ; Overrides `:rethrow?` - :rethrow? true} (+ 1 2)))] [(is (= rv 3)) (is (nil? sv))])]) + :rethrow? true} (+ 1 2)))] [(is (= rv 3)) (is (nil? sv))])]) #?(:clj (testing "uncaught->error!" @@ -453,13 +461,13 @@ [(do (enc/set-var-root! impl/*sig-handlers* [(sigs/wrap-handler "h1" (fn h1 [x] (reset! sv_ x)) nil {:async nil})]) :set-handler) ;; (is (nil? (tel/uncaught->error!))) - (is (do (.join (impl/threaded (throw ex1))) (sm? @sv_ {:kind :error, :line :submap/ex, :level :error, :error ex1-pred, :id nil}))) + (is (do (.join (impl/threaded (ex1!))) (sm? @sv_ {:kind :error, :line :submap/ex, :level :error, :error ex1-pred, :id nil}))) ;; (is (nil? (tel/uncaught->error! :id1))) - (is (do (.join (impl/threaded (throw ex1))) (sm? @sv_ {:kind :error, :line :submap/ex, :level :error, :error ex1-pred, :id :id1}))) + (is (do (.join (impl/threaded (ex1!))) (sm? @sv_ {:kind :error, :line :submap/ex, :level :error, :error ex1-pred, :id :id1}))) ;; (is (nil? (tel/uncaught->error! {:id :id1}))) - (is (do (.join (impl/threaded (throw ex1))) (sm? @sv_ {:kind :error, :line :submap/ex, :level :error, :error ex1-pred, :id :id1}))) + (is (do (.join (impl/threaded (ex1!))) (sm? @sv_ {:kind :error, :line :submap/ex, :level :error, :error ex1-pred, :id :id1}))) ;; (do (enc/set-var-root! impl/*sig-handlers* nil) :unset-handler)])))])