[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 (comment
[level-aliases] [level-aliases]
[handlers-help get-handlers add-handler! remove-handler! with-handler with-handler+] [help:handlers get-handlers add-handler! remove-handler! with-handler with-handler+]
[filtering-help get-filters get-min-level [help:filtering get-filters get-min-level
set-kind-filter! set-ns-filter! set-id-filter! set-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]) with-kind-filter with-ns-filter with-id-filter with-min-level])
@ -78,6 +78,14 @@
#?(:clj impl/with-signals) #?(:clj impl/with-signals)
#?(:clj impl/signal!)) #?(: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 ;;;; Context
(enc/defonce default-ctx (enc/defonce default-ctx
@ -88,7 +96,7 @@
(enc/get-env {:as :edn} :taoensso.telemere/default-ctx<.platform><.edn>)) (enc/get-env {:as :edn} :taoensso.telemere/default-ctx<.platform><.edn>))
(enc/def* ^:dynamic *ctx* (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. Value may be any type, but is usually nil or a map.
Re/bind dynamic value using `with-ctx`, `with-ctx+`, or `binding`. Re/bind dynamic value using `with-ctx`, `with-ctx+`, or `binding`.
@ -179,68 +187,120 @@
:data data})))) :data data}))))
;;;; Common signals ;;;; Common signals
;; - 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? ;; - 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 => given error
;; - error! [error] [id-or-opts error] ; error + ?id => error
;; - trace! [form ] [id-or-opts form] ; run + ?id => run result (value or throw) ;; - 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) ;; - 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 ;; - catch->error! [form ] [id-or-opts form] ; run + ?id => run value or ?return
;; - uncaught->error! [ ] [id-or-opts ] ; ?id => nil ;; - uncaught->error! [ ] [id-or-opts ] ; ?id => nil
#?(:clj #?(:clj
(defmacro log! (defmacro event!
"TODO Docstring [msg] [level-or-opts msg] => allowed?" "[id] [id level-or-opts] => allowed?"
{:arglists (impl/signal-arglists :log!)} {:doc (impl/signal-docstring :event!)
:arglists (impl/signal-arglists :event!)}
[& args] [& args]
(let [opts (apply impl/signal-opts :msg, :level, {:kind :log, :level :info} args)] (let [opts (impl/signal-opts `event! {:kind :event, :level :info} :id :level :dsc args)]
(enc/keep-callsite `(impl/signal! ~opts))))) (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 #?(:clj
(defmacro event! (defmacro log!
"TODO Docstring [id] [level-or-opts id] => allowed?" "[msg] [level-or-opts msg] => allowed?"
{:arglists (impl/signal-arglists :event!)} {:doc (impl/signal-docstring :log!)
:arglists (impl/signal-arglists :log!)}
[& args] [& args]
(let [opts (apply impl/signal-opts :id, :level, {:kind :event, :level :info} args)] (let [opts (impl/signal-opts `log! {:kind :log, :level :info} :msg :level :asc args)]
(enc/keep-callsite `(impl/signal! ~opts))))) (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 #?(:clj
(defmacro error! (defmacro error!
"TODO Docstring [error] [id-or-opts error] => error "[error] [error id-or-opts] => error"
(throw (error! <error>)) example." {:doc (impl/signal-docstring :error!)
{:arglists (impl/signal-arglists :error!)} :arglists (impl/signal-arglists :error!)}
[& args] [& 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)] error-form (get opts :error)]
;; (enc/keep-callsite `(impl/signal! ~opts)) ; => allowed?
(enc/keep-callsite (enc/keep-callsite
`(let [~'__error ~error-form] `(let [~'__error ~error-form]
(impl/signal! ~(assoc opts :error '__error)) (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 #?(:clj
(defmacro trace! (defmacro trace!
"TODO Docstring [form] [id-or-opts form] => run result (value or throw)" "[form] [id-or-opts form] => run result (value or throw)"
{:arglists (impl/signal-arglists :trace!)} {:doc (impl/signal-docstring :trace!)
:arglists (impl/signal-arglists :trace!)}
[& args] [& 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))))) (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 #?(:clj
(defmacro spy! (defmacro spy!
"TODO Docstring [form] [level-or-opts form] => run result (value or throw)" "[form] [level-or-opts form] => run result (value or throw)"
{:arglists (impl/signal-arglists :spy!)} {:doc (impl/signal-docstring :spy!)
:arglists (impl/signal-arglists :spy!)}
[& args] [& 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))))) (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 #?(:clj
(defmacro catch->error! (defmacro catch->error!
"TODO Docstring [form] [id-or-opts form] => run value or ?catch-val" "[form] [id-or-opts form] => run value or ?catch-val"
{:arglists (impl/signal-arglists :catch->error!)} {:doc (impl/signal-docstring :catch-to-error!)
:arglists (impl/signal-arglists :catch->error!)}
[& args] [& 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?)) rethrow? (if (contains? opts :catch-val) false (get opts :rethrow?))
catch-val (get opts :catch-val) catch-val (get opts :catch-val)
form (get opts ::__form) form (get opts ::__form)
@ -248,26 +308,38 @@
(enc/keep-callsite (enc/keep-callsite
`(enc/try* ~form `(enc/try* ~form
(catch :any ~'__t (catch :any ~'__caught-error
(impl/signal! ~(assoc opts :error '__t)) (impl/signal! ~(assoc opts :error '__caught-error))
(if ~rethrow? (throw ~'__t) ~catch-val))))))) (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 #?(:clj
(defmacro uncaught->error! (defmacro uncaught->error!
"TODO Docstring "Uses `uncaught->handler!` so that `error!` will be called for
See also `uncaught->handler!`." uncaught JVM errors.
See `uncaught->handler!` and `error!` for details."
{:arglists (impl/signal-arglists :uncaught->error!)} {:arglists (impl/signal-arglists :uncaught->error!)}
([ ] (enc/keep-callsite `(uncaught->error! nil))) [& args]
([id-or-opts]
(let [msg-form ["Uncaught Throwable on thread: " `(.getName ~(with-meta '__thread {:tag 'java.lang.Thread}))] (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)] opts
(impl/signal-opts `uncaught->error!
{:kind :error, :level :error, :msg msg-form}
:error :id :dsc (into ['__throwable] args))]
(enc/keep-callsite (enc/keep-callsite
`(uncaught->handler! `(uncaught->handler!
(fn [~'__thread ~'__throwable] (fn [~'__thread ~'__throwable]
(impl/signal! ~opts)))))))) (impl/signal! ~opts)))))))
(comment (macroexpand '(uncaught->error! :id1))) (comment (macroexpand '(uncaught->error! :id1)))
@ -277,12 +349,14 @@
(defn uncaught->handler! (defn uncaught->handler!
"Sets JVM's global `DefaultUncaughtExceptionHandler` to given "Sets JVM's global `DefaultUncaughtExceptionHandler` to given
(fn handler [`<java.lang.Thread>` `<java.lang.Throwable>`]). (fn handler [`<java.lang.Thread>` `<java.lang.Throwable>`]).
See also `uncaught->error!`." See also `uncaught->error!`."
[handler] [handler]
(Thread/setDefaultUncaughtExceptionHandler (Thread/setDefaultUncaughtExceptionHandler
(reify Thread$UncaughtExceptionHandler (reify Thread$UncaughtExceptionHandler
(uncaughtException [_ thread throwable] (uncaughtException [_ thread throwable]
(handler thread throwable)))))) (handler thread throwable))))
nil))
#?(:clj #?(:clj
(defn hostname (defn hostname

View file

@ -194,7 +194,7 @@
(defrecord Signal (defrecord Signal
;; Telemere's main public data type, we avoid nesting and duplication ;; Telemere's main public data type, we avoid nesting and duplication
[^long schema-version instant uid, [^long schema-version instant uid,
callsite-id location ns line column file, location ns line column file,
sample-rate, kind id level, ctx parent, sample-rate, kind id level, ctx parent,
data msg_ error run-form run-value, data msg_ error run-form run-value,
end-instant runtime-nsecs]) end-instant runtime-nsecs])
@ -256,7 +256,7 @@
#?(:clj #?(:clj
(defmacro ^:public with-signals (defmacro ^:public with-signals
"Executes given form and records any signals triggered by it. "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: Options:
@ -301,7 +301,7 @@
^Signal ^Signal
;; Note all dynamic vals passed as explicit args for better control ;; Note all dynamic vals passed as explicit args for better control
[instant uid, [instant uid,
callsite-id location ns line column file, location ns line column file,
sample-rate, kind id level, ctx parent, sample-rate, kind id level, ctx parent,
user-opts data msg_, user-opts data msg_,
run-form run-result error] run-form run-result error]
@ -321,14 +321,14 @@
msg_)] msg_)]
(Signal. 1 instant uid, (Signal. 1 instant uid,
callsite-id location ns line column file, location ns line column file,
sample-rate, kind id level, ctx parent, sample-rate, kind id level, ctx parent,
data msg_, data msg_,
run-error run-form run-value, run-error run-form run-value,
end-instant runtime-nsecs)) end-instant runtime-nsecs))
(Signal. 1 instant uid, (Signal. 1 instant uid,
callsite-id location ns line column file, location ns line column file,
sample-rate, kind id level, ctx parent, sample-rate, kind id level, ctx parent,
data msg_, error nil nil instant nil))] data msg_, error nil nil instant nil))]
@ -340,66 +340,69 @@
(enc/qb 1e6 ; 55.67 (enc/qb 1e6 ; 55.67
(new-signal (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 nil))) nil nil nil nil nil nil nil nil nil)))
;;;; Signal API helpers ;;;; 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 #?(:clj
(defn signal-arglists [macro-id] (defn signal-arglists [macro-id]
(case macro-id (case macro-id
:signal! ; [opts] => <run result> or <allowed?> :signal! ; [opts] => allowed? / run result (value or throw)
'([{:as opts :keys '([{:as opts :keys
[#_defaults #_elide? #_allow? #_callsite-id, [#_defaults #_elide? #_allow? #_expansion-id, ; Undocumented
elidable? location instant uid middleware, elidable? location instant uid middleware,
sample-rate ns kind id level when rate-limit, sample-rate ns kind id level when rate-limit,
ctx parent trace?, do let data msg error run & user-opts]}]) 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] '([ msg]
[level msg] [level msg]
[{:as opts :keys [{:as opts :keys
[#_defaults #_elide? #_allow? #_callsite-id, [#_defaults #_elide? #_allow? #_expansion-id,
elidable? location instant uid middleware, elidable? location instant uid middleware,
sample-rate ns kind id level when rate-limit, sample-rate ns kind id level when rate-limit,
ctx parent trace?, do let data msg error #_run & user-opts]} ctx parent trace?, do let data msg error #_run & user-opts]}
msg]) msg])
:event! ; [id] [level-or-opts id] => <allowed?> :error! ; [error] [id-or-opts error] => given error
'([ 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 error] [id error]
[{:as opts :keys [{:as opts :keys
[#_defaults #_elide? #_allow? #_callsite-id, [#_defaults #_elide? #_allow? #_expansion-id,
elidable? location instant uid middleware, elidable? location instant uid middleware,
sample-rate ns kind id level when rate-limit, sample-rate ns kind id level when rate-limit,
ctx parent trace?, do let data msg error #_run & user-opts]} ctx parent trace?, do let data msg error #_run & user-opts]}
error]) 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] '([ form]
[id form] [id form]
[{:as opts :keys [{:as opts :keys
[#_defaults #_elide? #_allow? #_callsite-id, [#_defaults #_elide? #_allow? #_expansion-id,
elidable? location instant uid middleware, elidable? location instant uid middleware,
sample-rate ns kind id level when rate-limit, sample-rate ns kind id level when rate-limit,
ctx parent trace?, do let data msg error run & user-opts]} ctx parent trace?, do let data msg error run & user-opts]}
form]) form])
:catch->error! ; [form] [level-or-opts form] => <run result> (value or throw) :catch->error! ; [form] [id-or-opts form] => run result (value or throw)
'([ form] '([ form]
[level form] [id form]
[{:as opts :keys [{:as opts :keys
[#_defaults #_elide? #_allow? #_callsite-id, rethrow? catch-val, [#_defaults #_elide? #_allow? #_expansion-id, rethrow? catch-val,
elidable? location instant uid middleware, elidable? location instant uid middleware,
sample-rate ns kind id level when rate-limit, sample-rate ns kind id level when rate-limit,
ctx parent trace?, do let data msg error #_run & user-opts]} ctx parent trace?, do let data msg error #_run & user-opts]}
@ -409,7 +412,7 @@
'([ ] '([ ]
[id] [id]
[{:as opts :keys [{:as opts :keys
[#_defaults #_elide? #_allow? #_callsite-id, [#_defaults #_elide? #_allow? #_expansion-id,
elidable? location instant uid middleware, elidable? location instant uid middleware,
sample-rate ns kind id level when rate-limit, sample-rate ns kind id level when rate-limit,
ctx parent trace?, do let data msg error #_run & user-opts]}]) ctx parent trace?, do let data msg error #_run & user-opts]}])
@ -418,48 +421,46 @@
#?(:clj #?(:clj
(defn signal-opts (defn signal-opts
"Util to help write common signal wrapper macros: "Util to help write common signal wrapper macros."
[[<config>] val-y] => signal-opts [context defaults main-key extra-key arg-order args]
[[<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})))))
(comment (enc/cond
[(signal-opts :msg :level {:level :info} "foo") :let [num-args (count args)]
(signal-opts :msg :level {:level :info} {:level :warn} "foo")
(signal-opts :msg :level {:level :info} :warn "foo")]) (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 ;;;; Signal macro
#?(:clj #?(:clj
(defmacro ^:public signal! (defmacro ^:public signal!
"Expands to a low-level signal call. "Generic low-level signal call, also aliased in Encore."
{:doc (signal-docstring :signal!)
TODO Docstring :arglists (signal-arglists :signal!)}
- 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!)}
[opts] [opts]
(have? map? opts) ; We require const map keys, but vals may require eval (have? map? opts) ; We require const map keys, but vals may require eval
(let [defaults (get opts :defaults) (let [defaults (get opts :defaults)
opts (merge defaults (dissoc opts :defaults)) opts (merge defaults (dissoc opts :defaults))
{run-form :run} opts {run-form :run} opts
{:keys [callsite-id location elide? allow?]} {:keys [#_expansion-id location elide? allow?]}
(sigs/filterable-expansion (sigs/filterable-expansion
{:macro-form &form {:macro-form &form
:macro-env &env :macro-env &env
@ -510,14 +511,14 @@
:elidable? :location :instant :uid :middleware, :elidable? :location :instant :uid :middleware,
:sample-rate :ns :kind :id :level :filter :when #_:rate-limit, :sample-rate :ns :kind :id :level :filter :when #_:rate-limit,
:ctx :parent #_:trace?, :do :let :data :msg :error :run :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 ;; Eval let bindings AFTER call filtering but BEFORE data, msg
`(do `(do
~do-form ~do-form
(let ~let-form ; Allow to throw during `signal-value_` deref (let ~let-form ; Allow to throw during `signal-value_` deref
(new-signal ~'__instant ~'__uid (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, ~sample-rate-form, ~kind-form ~'__id ~level-form, ~ctx-form ~parent-form,
~user-opts-form ~data-form ~msg-form, ~user-opts-form ~data-form ~msg-form,
'~run-form ~'__run-result ~error-form))))] '~run-form ~'__run-result ~error-form))))]
@ -572,7 +573,7 @@
"Used only for interop (SLF4J, `clojure.tools.logging`, etc.)." "Used only for interop (SLF4J, `clojure.tools.logging`, etc.)."
{:arglists (signal-arglists :signal!)} {:arglists (signal-arglists :signal!)}
[opts] [opts]
(let [{:keys [#_callsite-id #_location elide? allow?]} (let [{:keys [#_expansion-id #_location elide? allow?]}
(sigs/filterable-expansion (sigs/filterable-expansion
{:macro-form &form {:macro-form &form
:macro-env &env :macro-env &env

View file

@ -10,7 +10,7 @@
#?(:cljs #?(:cljs
(:require-macros (:require-macros
[taoensso.telemere-tests :refer [sig! ws ws! ws1]]))) [taoensso.telemere-tests :refer [sig! ws wsf wst ws1]])))
(comment (comment
(remove-ns 'taoensso.telemere-tests) (remove-ns 'taoensso.telemere-tests)
@ -25,12 +25,14 @@
#?(:clj #?(:clj
(do (do
(defmacro ws [form] `(impl/-with-signals (fn [] ~form) {})) (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#)))) (defmacro ws1 [form] `(let [[_# [s1#]] (impl/-with-signals (fn [] ~form) {:force-msg? true})] s1#))))
(do (do
(def ex1 (ex-info "TestEx" {})) (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) (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 })) [nil nil]) "With runtime suppression")
(is (= (ws (sig! {:level :info, :allow? false, :run (+ 1 2)})) [3 nil]) "With runtime suppression, run-form") (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, :elide? true, :run (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, :allow? false, :run (ex1!)}) (throws? :ex-info "TestEx")) "With runtime suppression, throwing run-form")
(let [[rv1 [sv1]] (ws (sig! {:level :info })) (let [[rv1 [sv1]] (ws (sig! {:level :info }))
[rv2 [sv2]] (ws (sig! {:level :info, :run (+ 1 2)}))] [rv2 [sv2]] (ws (sig! {:level :info, :run (+ 1 2)}))]
@ -281,25 +283,25 @@
(tel/with-handler :hid1 (tel/with-handler :hid1
(fn [sv] (force (:data sv)) (reset! sv_ sv)) (fn [sv] (force (:data sv)) (reset! sv_ sv))
{:async nil, :error-fn (fn [x] (reset! error_ x)), :rl-error nil, {: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, :when (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, :instant (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, :id (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, :uid (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, :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") (is (sm? @sv_ {:level :info, :error ex1-pred}) "`~run-form` rethrows at call *after* dispatch")
(testing "`@signal-value_`: trap with wrapped handler" (testing "`@signal-value_`: trap with wrapped handler"
[(testing "Throwing `~let-form`" [(testing "Throwing `~let-form`"
(reset-state!) (reset-state!)
[(is (true? (sig! {:level :info, :let [_ (throw ex1)]}))) [(is (true? (sig! {:level :info, :let [_ (ex1!)]})))
(is (= @sv_ :nx)) (is (= @sv_ :nx))
(is (sm? @error_ {:handler-id :hid1, :error ex1-pred}))]) (is (sm? @error_ {:handler-id :hid1, :error ex1-pred}))])
(testing "Throwing call middleware" (testing "Throwing call middleware"
(reset-state!) (reset-state!)
[(is (true? (sig! {:level :info, :middleware [(fn [_] (throw ex1))]}))) [(is (true? (sig! {:level :info, :middleware [(fn [_] (ex1!))]})))
(is (= @sv_ :nx)) (is (= @sv_ :nx))
(is (sm? @error_ {:handler-id :hid1, :error ex1-pred}))]) (is (sm? @error_ {:handler-id :hid1, :error ex1-pred}))])
@ -312,13 +314,13 @@
(testing "Throwing `@data_`" (testing "Throwing `@data_`"
(reset-state!) (reset-state!)
[(is (true? (sig! {:level :info, :data (delay (throw ex1))}))) [(is (true? (sig! {:level :info, :data (delay (ex1!))})))
(is (= @sv_ :nx)) (is (= @sv_ :nx))
(is (sm? @error_ {:handler-id :hid1, :error ex1-pred}))]) (is (sm? @error_ {:handler-id :hid1, :error ex1-pred}))])
(testing "Throwing user opt" (testing "Throwing user opt"
(reset-state!) (reset-state!)
[(is (true? (sig! {:level :info, :my-opt (throw ex1)}))) [(is (true? (sig! {:level :info, :my-opt (ex1!)})))
(is (= @sv_ :nx)) (is (= @sv_ :nx))
(is (sm? @error_ {:handler-id :hid1, :error ex1-pred}))])])]))) (is (sm? @error_ {:handler-id :hid1, :error ex1-pred}))])])])))
@ -400,21 +402,19 @@
(deftest _common-signals (deftest _common-signals
[#?(:clj [#?(:clj
(testing "signal-opts" (testing "signal-opts"
[(is (= (impl/signal-opts :msg, :level, {:level :info} "msg") {:defaults {:level :info}, :msg "msg"})) [(is (= (impl/signal-opts `signal! {:level :info} :id :level :dsc [::my-id ]) {:defaults {:level :info}, :id ::my-id}))
(is (= (impl/signal-opts :msg, :level, {:level :info} {:level :warn} "msg") {:defaults {:level :info}, :msg "msg", :level :warn})) (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 :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 {:level :warn}]) {:defaults {:level :info}, :id ::my-id, :level :warn}))
(testing "log!" ; msg + ?level => allowed? (is (= (impl/signal-opts `signal! {:level :info} :id :level :asc [ ::my-id]) {:defaults {:level :info}, :id ::my-id}))
[(let [[rv [sv]] (ws (tel/log! "msg"))] [(is (= rv true)) (is (sm? sv {:kind :log, :line :submap/ex, :msg_ "msg", :level :info}))]) (is (= (impl/signal-opts `signal! {:level :info} :id :level :asc [:warn ::my-id]) {:defaults {:level :info}, :id ::my-id, :level :warn}))
(let [[rv [sv]] (ws (tel/log! :warn "msg"))] [(is (= rv true)) (is (sm? sv {:kind :log, :line :submap/ex, :msg_ "msg", :level :warn}))]) (is (= (impl/signal-opts `signal! {:level :info} :id :level :asc [{:level :warn} ::my-id]) {:defaults {:level :info}, :id ::my-id, :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 "event!" ; id + ?level => allowed? (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! :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! :id1 :warn ))] [(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! :id1 {:level :warn}))] [(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 {:allow? false}))] [(is (= rv nil)) (is (nil? sv))])])
(testing "error!" ; error + ?id => error (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}))]) [(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,27 +422,35 @@
(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! {: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))])]) (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) (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]] (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! :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 [[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 [[[_ 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))])]) (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) (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]] (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! :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 [[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 [[[_ 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))])]) (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 (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! (+ 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! (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! :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} (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} (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 [[[_ 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} (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} (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} (+ 1 2)))] [(is (= rv 3)) (is (nil? sv))])
(let [[rv [sv]] (ws (tel/catch->error! {:catch-val :foo ; Overrides `:rethrow?` (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))])])
@ -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) [(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 (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 (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 (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)])))]) (do (enc/set-var-root! impl/*sig-handlers* nil) :unset-handler)])))])