[new] Misc API polish, first signal docstrings

This commit is contained in:
Peter Taoussanis 2024-03-11 09:49:56 +01:00
parent ef79a57e48
commit 5a0d9d8241
13 changed files with 570 additions and 163 deletions

View file

@ -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 <caught> ...}
(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

View file

@ -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 <MyEx> ...}
(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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 ∈ #{<int> :trace :debug :info :warn :error :fatal :report ...}
`:kind` - Signal ?kind ∈ #{nil :event :error :log :trace :spy <user-val> ...}
`: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`

View file

@ -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. <https://tinyurl.com/telemere-signal-flowchart>
If anything is unclear, please ping me (@ptaoussanis) so that I can
improve these docs!

View file

@ -0,0 +1,33 @@
Signal options (shared by `signal!`, `event!`, ...):
`:instant` - Platform instant [1] when signal was created, ∈ #{nil :auto <user-val>}
`:level` - Signal level ∈ #{<int> :trace :debug :info :warn :error :fatal :report ...}
`:kind` - Signal ?kind ∈ #{nil :event :error :log :trace :spy <user-val> ...}
`: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?
<user-opts> - 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`

View file

@ -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

View file

@ -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

View file

@ -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! <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 [`<java.lang.Thread>` `<java.lang.Throwable>`]).
See also `uncaught->error!`."
[handler]
(Thread/setDefaultUncaughtExceptionHandler
(reify Thread$UncaughtExceptionHandler
(uncaughtException [_ thread throwable]
(handler thread throwable))))))
(handler thread throwable))))
nil))
#?(:clj
(defn hostname

View file

@ -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] => <run result> or <allowed?>
: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] => <allowed?>
: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] => <allowed?>
'([ 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! ; [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] => <run result> (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] => <run result> (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:
[[<config>] val-y] => signal-opts
[[<config>] 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. <https://tinyurl.com/telemere-signal-flow>"
{: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

View file

@ -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)])))])