[new] Refactor impln of common signal creators

Objectives:

  - Support single map opts arg in all cases.

  - Make it easier for folks to inspect the source to understand how
    the common creators use/wrap underlying `signal!`.

Also updated relevant docstrings, etc.
This commit is contained in:
Peter Taoussanis 2024-12-21 11:11:47 +01:00
parent ace6e2dd2c
commit d2386d62f1
12 changed files with 265 additions and 376 deletions

View file

@ -1,10 +1,9 @@
Unconditionally executes given form and- ALWAYS (unconditionally) executes given `run` form and:
If form succeeds: return the form's result. If `run` form succeeds: return the form's result.
If form throws: If `run` form throws:
Call `error!` with the thrown error and the given signal options [2], Call `error!` with the thrown error and the given signal options [2],
then return (:catch-val opts) if it exists, or rethrow the error. 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 kind: `:error`
Default level: `:error` Default level: `:error`

View file

@ -1,6 +1,7 @@
"Error" signal creator, emphasizing error + id. "Error" signal creator, emphasizing (optional id) + error (Exception, etc.).
ALWAYS (unconditionally) returns the given error, so can conveniently be wrapped
by `throw`: (throw (error! (ex-info ...)), etc.
API: [error] [id-or-opts error] => given error (unconditional)
Default kind: `:error` Default kind: `:error`
Default level: `:error` Default level: `:error`
@ -22,7 +23,6 @@ Tips:
- Supports the same options [2] as other signals [1]. - Supports the same options [2] as other signals [1].
- `error` arg is a platform error (`java.lang.Throwable` or `js/Error`). - `error` arg is a platform error (`java.lang.Throwable` or `js/Error`).
- Can conveniently be wrapped by `throw`: (throw (error! ...)).
---------------------------------------------------------------------- ----------------------------------------------------------------------
[1] See `help:signal-creators` - (`signal!`, `log!`, `event!`, ...) [1] See `help:signal-creators` - (`signal!`, `log!`, `event!`, ...)

View file

@ -1,6 +1,6 @@
"Event" signal creator, emphasizing id + level. "Event" signal creator, emphasizing id + (optional level).
Returns true iff signal was created (allowed by filtering).
API: [id] [id level-or-opts] => true iff signal was allowed
Default kind: `:event` Default kind: `:event`
Default level: `:info` Default level: `:info`

View file

@ -1,6 +1,6 @@
"Log" signal creator, emphasizing message + level. "Log" signal creator, emphasizing (optional level) + message.
Returns true iff signal was created (allowed by filtering).
API: [msg] [level-or-opts msg] => true iff signal was allowed.
Default kind: `:log` Default kind: `:log`
Default level: `:info` Default level: `:info`

View file

@ -1,16 +1,17 @@
Low-level generic signal creator. Low-level "generic" signal creator for creating signals of any "kind".
Takes a single map of options [2] with compile-time keys.
API: [opts] => depends on options [2] Return value depends on options:
Default kind: `:generic` - If given `:run` form: unconditionally returns run value, or rethrows run error.
- Otherwise: returns true iff signal was created (allowed by filtering).
Default kind: `:generic` (feel free to change!)
Default level: `:info` Default level: `:info`
When filtering conditions are met [4], creates a Telemere signal [3] and When filtering conditions are met [4], creates a Telemere signal [3] and
dispatches it to registered handlers for processing (e.g. writing to dispatches it to registered handlers for processing (e.g. writing to
console/file/queue/db, etc.). console/file/queue/db, etc.).
If `:run` option is provided: returns value of given run form, or throws.
Otherwise: returns true iff signal was created (allowed).
Generic signals are fairly low-level and useful mostly for library authors or Generic signals are fairly low-level and useful mostly for library authors or
advanced users writing their own wrapper macros. Regular users will typically advanced users writing their own wrapper macros. Regular users will typically
prefer one of the higher-level signal creators optimized for ease-of-use in prefer one of the higher-level signal creators optimized for ease-of-use in

View file

@ -14,13 +14,13 @@ various keys:
Creators vary only in in their default options and call APIs (expected args Creators vary only in in their default options and call APIs (expected args
and return values), making them more/less convenient for certain use cases: and return values), making them more/less convenient for certain use cases:
`event!` -------- [id ] or [id opts/level] => true iff signal was created (allowed) `signal!` ------- opts => allowed? / unconditional run result (value or throw)
`log!` ---------- [msg ] or [opts/level msg] => true iff signal was created (allowed) `event!` -------- id + ?level => allowed?
`error!` -------- [error] or [opts/id error] => given error (unconditional) `log!` ---------- ?level + msg => allowed?
`trace!` -------- [form ] or [opts/id form] => form result (value/throw) (unconditional) `trace!` -------- ?id + run => unconditional run result (value or throw)
`spy!` ---------- [form ] or [opts/level form] => form result (value/throw) (unconditional) `spy!` ---------- ?level + run => unconditional run result (value or throw)
`catch->error!` - [form ] or [opts/id form] => form value, or given fallback `error!` -------- ?id + error => unconditional given error
`signal!` ------- [opts ] => depends on options `catch->error!` - ?id + run => unconditional run value or ?catch-val
- `log!` and `event!` are both good default/general-purpose signal creators. - `log!` and `event!` are both good default/general-purpose signal creators.
- `log!` emphasizes messages, while `event!` emphasizes ids. - `log!` emphasizes messages, while `event!` emphasizes ids.

View file

@ -1,6 +1,6 @@
"Spy" signal creator, emphasizing form + level. "Spy" signal creator, emphasizing (optional level) + form to run.
ALWAYS (unconditionally) returns run value, or rethrows run error.
API: [form] [level-or-opts form] => form's result (value/throw) (unconditional)
Default kind: `:spy` Default kind: `:spy`
Default level: `:info` Default level: `:info`
@ -8,14 +8,14 @@ When filtering conditions are met [4], creates a Telemere signal [3] and
dispatches it to registered handlers for processing (e.g. writing to dispatches it to registered handlers for processing (e.g. writing to
console/file/queue/db, etc.). console/file/queue/db, etc.).
Enables tracing of given `form` arg: Enables tracing of given `run` form:
- Resulting signal will include {:keys [run-form run-val run-nsecs]}. - Resulting signal will include {:keys [run-form run-val run-nsecs]}.
- Nested signals will include this signal's id and uid under `:parent`. - Nested signals will include this signal's id and uid under `:parent`.
Limitations: Limitations:
1. Traced code (`form` arg) is usually expected to be synchronous and eager. 1. Traced `run` form is usually expected to be synchronous and eager.
So no lazy seqs, async calls, or inversion of flow control (IoC) macros like So no lazy seqs, async calls, or inversion of flow control (IoC) macros like
core.async `go` blocks, etc. core.async `go` blocks, etc.
@ -47,18 +47,18 @@ Tips:
- Test using `with-signal`: (with-signal (spy! ...)). - Test using `with-signal`: (with-signal (spy! ...)).
- Supports the same options [2] as other signals [1]. - Supports the same options [2] as other signals [1].
- Identical to `trace!`, but emphasizes form + level rather than form + id. - Like `trace!`, but takes optional level rather than optional id.
- Useful for debugging/monitoring forms, and tracing (nested) execution flow. - Useful for debugging/monitoring forms, and tracing (nested) execution flow.
- Execution of `form` arg may create additional (nested) signals. - Execution of `run` form may create additional (nested) signals.
Each signal's `:parent` key will indicate its immediate parent. Each signal's `:parent` key will indicate its immediate parent.
- Can be useful to wrap with `catch->error!`: - Can be useful to wrap with `catch->error!`:
(catch->error! ::error-id (spy! ...)). (catch->error! ::error-id (spy! ...)).
- Runtime of async or lazy code in `form` will intentionally NOT be included - Runtime of async or lazy code in `run` form will intentionally NOT be
in resulting signal's `:run-nsecs` value. If you want to measure such included in resulting signal's `:run-nsecs` value. If you want to measure
runtimes, make sure that your form wraps where the relevant costs are such runtimes, make sure that your form wraps where the relevant costs are
actually realized. Compare: actually realized. Compare:
(spy! (delay (my-slow-code))) ; Doesn't measure slow code (spy! (delay (my-slow-code))) ; Doesn't measure slow code
(spy! @(delay (my-slow-code))) ; Does measure slow code (spy! @(delay (my-slow-code))) ; Does measure slow code

View file

@ -1,6 +1,6 @@
"Trace" signal creator, emphasizing form + id. "Trace" signal creator, emphasizing (optional id) + form to run.
ALWAYS (unconditionally) returns run value, or rethrows run error.
API: [form] [id-or-opts form] => form's result (value/throw) (unconditional)
Default kind: `:trace` Default kind: `:trace`
Default level: `:info` (intentionally NOT `:trace`!) Default level: `:info` (intentionally NOT `:trace`!)
@ -8,14 +8,14 @@ When filtering conditions are met [4], creates a Telemere signal [3] and
dispatches it to registered handlers for processing (e.g. writing to dispatches it to registered handlers for processing (e.g. writing to
console/file/queue/db, etc.). console/file/queue/db, etc.).
Enables tracing of given `form` arg: Enables tracing of given `run` form:
- Resulting signal will include {:keys [run-form run-val run-nsecs]}. - Resulting signal will include {:keys [run-form run-val run-nsecs]}.
- Nested signals will include this signal's id and uid under `:parent`. - Nested signals will include this signal's id and uid under `:parent`.
Limitations: Limitations:
1. Traced code (`form` arg) is usually expected to be synchronous and eager. 1. Traced `run` form is usually expected to be synchronous and eager.
So no lazy seqs, async calls, or inversion of flow control (IoC) macros like So no lazy seqs, async calls, or inversion of flow control (IoC) macros like
core.async `go` blocks, etc. core.async `go` blocks, etc.
@ -47,10 +47,10 @@ Tips:
- Test using `with-signal`: (with-signal (trace! ...)). - Test using `with-signal`: (with-signal (trace! ...)).
- Supports the same options [2] as other signals [1]. - Supports the same options [2] as other signals [1].
- Identical to `spy!`, but emphasizes form + id rather than form + level. - Like `spy!`, but takes optional id rather than optional level.
- Useful for debugging/monitoring forms, and tracing (nested) execution flow. - Useful for debugging/monitoring forms, and tracing (nested) execution flow.
- Execution of `form` arg may create additional (nested) signals. - Execution of `run` form may create additional (nested) signals.
Each signal's `:parent` key will indicate its immediate parent. Each signal's `:parent` key will indicate its immediate parent.
- Can be useful to wrap with `catch->error!`: - Can be useful to wrap with `catch->error!`:
@ -60,9 +60,9 @@ Tips:
refers to the general action of tracing program flow rather than to the refers to the general action of tracing program flow rather than to the
common logging level of the same name. common logging level of the same name.
- Runtime of async or lazy code in `form` will intentionally NOT be included - Runtime of async or lazy code in `run` form will intentionally NOT be
in resulting signal's `:run-nsecs` value. If you want to measure such included in resulting signal's `:run-nsecs` value. If you want to measure
runtimes, make sure that your form wraps where the relevant costs are such runtimes, make sure that your form wraps where the relevant costs are
actually realized. Compare: actually realized. Compare:
(trace! (delay (my-slow-code))) ; Doesn't measure slow code (trace! (delay (my-slow-code))) ; Doesn't measure slow code
(trace! @(delay (my-slow-code))) ; Does measure slow code (trace! @(delay (my-slow-code))) ; Does measure slow code

View file

@ -198,154 +198,102 @@
(comment (enc/qb 1e6 (force *otel-tracer*))) ; 51.23 (comment (enc/qb 1e6 (force *otel-tracer*))) ; 51.23
;;;; Signal creators ;;;; Signal creators
;; - event! [id ] [id opts/level] ; id + ?level => allowed? ; Sole signal with descending main arg! ;; - signal! ---------- opts => allowed? / unconditional run result (value or throw)
;; - log! [msg ] [opts/level msg] ; msg + ?level => allowed? ;; - event! ----------- id + ?level => allowed?
;; - error! [error] [opts/id error] ; error + ?id => given error ;; - log! ------------- ?level + msg => allowed?
;; - trace! [form ] [opts/id form] ; run + ?id => run result (value or throw) ;; - trace! ----------- ?id + run => unconditional run result (value or throw)
;; - spy! [form ] [opts/level form] ; run + ?level => run result (value or throw) ;; - spy! ------------- ?level + run => unconditional run result (value or throw)
;; - catch->error! [form ] [opts/id form] ; run + ?id => run value or ?return ;; - error! ----------- ?id + error => unconditional given error
;; - signal! [opts ] ; => allowed? / run result (value or throw) ;; - catch->error! ---- ?id + run => unconditional run value or ?catch-val
;; - uncaught->error! [opts/id] ; ?id => nil ;; - uncaught->error! - ?id => nil
#?(:clj #?(:clj
(defn- merge-or-assoc-opts [m &form &env k v]
(let [m (assoc m :location* (enc/get-source &form &env))]
(if (map? v)
(merge m v)
(assoc m k v)))))
#?(:clj
(let [base-opts {:kind :event, :level :info}]
(defmacro event! (defmacro event!
"[id] [id level-or-opts] => allowed?" "id + ?level => allowed? Note unique arg order: [x opts] rather than [opts x]!"
{:doc (impl/signal-docstring :event!) {:doc (impl/signal-docstring :event!)
:arglists (impl/signal-arglists :event!)} :arglists (impl/signal-arglists :event!)}
[& args] ([ opts-or-id] `(impl/signal! ~(merge-or-assoc-opts base-opts &form &env :id opts-or-id)))
(let [opts ([id opts-or-level] `(impl/signal! ~(assoc (merge-or-assoc-opts base-opts &form &env :level opts-or-level) :id id))))))
(impl/signal-opts `event! (enc/get-source &form &env)
{:kind :event, :level :info} :id :level :dsc args)]
`(impl/signal! ~opts))))
(comment (with-signal (event! ::my-id :info))) (comment (with-signal (event! ::my-id :info)))
#?(:clj #?(:clj
(let [base-opts {:kind :log, :level :info}]
(defmacro log! (defmacro log!
"[msg] [level-or-opts msg] => allowed?" "?level + msg => allowed?"
{:doc (impl/signal-docstring :log!) {:doc (impl/signal-docstring :log!)
:arglists (impl/signal-arglists :log!)} :arglists (impl/signal-arglists :log!)}
[& args] ([opts-or-msg ] `(impl/signal! ~(merge-or-assoc-opts base-opts &form &env :msg opts-or-msg)))
(let [opts ([opts-or-level msg] `(impl/signal! ~(assoc (merge-or-assoc-opts base-opts &form &env :level opts-or-level) :msg msg))))))
(impl/signal-opts `log! (enc/get-source &form &env)
{:kind :log, :level :info} :msg :level :asc args)]
`(impl/signal! ~opts))))
(comment (with-signal (log! :info "My msg"))) (comment (with-signal (log! :info "My msg")))
#?(:clj #?(:clj
(let [base-opts {:kind :trace, :level :info, :msg `impl/default-trace-msg}]
(defmacro trace!
"?id + run => unconditional run result (value or throw)."
{:doc (impl/signal-docstring :trace!)
:arglists (impl/signal-arglists :trace!)}
([opts-or-run] `(impl/signal! ~(merge-or-assoc-opts base-opts &form &env :run opts-or-run)))
([opts-or-id run] `(impl/signal! ~(assoc (merge-or-assoc-opts base-opts &form &env :id opts-or-id) :run run))))))
(comment (with-signal (trace! ::my-id (+ 1 2))))
#?(:clj
(let [base-opts {:kind :spy, :level :info, :msg `impl/default-trace-msg}]
(defmacro spy!
"?level + run => unconditional run result (value or throw)."
{:doc (impl/signal-docstring :spy!)
:arglists (impl/signal-arglists :spy!)}
([opts-or-run] `(impl/signal! ~(merge-or-assoc-opts base-opts &form &env :run opts-or-run)))
([opts-or-level run] `(impl/signal! ~(assoc (merge-or-assoc-opts base-opts &form &env :level opts-or-level) :run run))))))
(comment (with-signals (spy! :info (+ 1 2))))
#?(:clj
(let [base-opts {:kind :error, :level :error}]
(defmacro error! (defmacro error!
"[error] [error id-or-opts] => error" "?id + error => unconditional given error."
{:doc (impl/signal-docstring :error!) {:doc (impl/signal-docstring :error!)
:arglists (impl/signal-arglists :error!)} :arglists (impl/signal-arglists :error!)}
[& args] ([opts-or-id error] `(error! ~(assoc (merge-or-assoc-opts base-opts &form &env :id opts-or-id) :error error)))
(let [opts ([opts-or-error]
(impl/signal-opts `error! (enc/get-source &form &env) (let [opts (merge-or-assoc-opts base-opts &form &env :error opts-or-error)]
{:kind :error, :level :error} :error :id :asc args) `(let [~'__error ~(get opts :error)]
error-form (get opts :error)]
`(let [~'__error ~error-form]
(impl/signal! ~(assoc opts :error '__error)) (impl/signal! ~(assoc opts :error '__error))
~'__error ; Unconditional! (do ~'__error)))))))
))))
(comment (with-signal (throw (error! ::my-id (ex-info "MyEx" {}))))) (comment (with-signal (throw (error! ::my-id (ex-info "MyEx" {})))))
#?(:clj #?(:clj
(let [base-opts {:kind :error, :level :error}]
(defmacro catch->error! (defmacro catch->error!
"[form] [id-or-opts form] => run value or ?catch-val" "?id + run => unconditional run value or ?catch-val."
{:doc (impl/signal-docstring :catch-to-error!) {:doc (impl/signal-docstring :catch->error!)
:arglists (impl/signal-arglists :catch->error!)} :arglists (impl/signal-arglists :catch->error!)}
[& args] ([opts-or-id run] `(catch->error! ~(assoc (merge-or-assoc-opts base-opts &form &env :id opts-or-id) :run run)))
(let [opts ([opts-or-run]
(impl/signal-opts `catch->error! (enc/get-source &form &env) (let [opts (merge-or-assoc-opts base-opts &form &env :run opts-or-run)
{:kind :error, :level :error} ::__form :id :asc args)
rethrow? (if (contains? opts :catch-val) false (get opts :rethrow? true)) rethrow? (if (contains? opts :catch-val) false (get opts :rethrow? true))
catch-val (get opts :catch-val) catch-val (get opts :catch-val)
catch-sym (get opts :catch-sym '__caught-error) ; Undocumented catch-sym (get opts :catch-sym '__caught-error) ; Undocumented
form (get opts ::__form) run-form (get opts :run)
opts (dissoc opts ::__form :catch-val :catch-sym :rethrow?)] opts (dissoc opts :run :catch-val :catch-sym :rethrow?)]
`(enc/try* ~form `(enc/try* ~run-form
(catch :all ~catch-sym (catch :all ~catch-sym
(impl/signal! ~(assoc opts :error catch-sym)) (impl/signal! ~(assoc opts :error catch-sym))
(if ~rethrow? (throw ~catch-sym) ~catch-val)))))) (if ~rethrow? (throw ~catch-sym) ~catch-val))))))))
(comment (comment (with-signal (catch->error! ::my-id (/ 1 0))))
(with-signal (catch->error! ::my-id (/ 1 0)))
(with-signal (catch->error! { :msg ["Error:" __caught-error]} (/ 1 0)))
(with-signal (catch->error! {:catch-sym my-err :msg ["Error:" my-err]} (/ 1 0))))
#?(:clj
(defmacro trace!
"[form] [id-or-opts form] => run result (value or throw)"
{:doc (impl/signal-docstring :trace!)
:arglists (impl/signal-arglists :trace!)}
[& args]
(let [opts
(impl/signal-opts `trace! (enc/get-source &form &env)
{:kind :trace, :level :info, :msg `impl/default-trace-msg}
:run :id :asc args)
;; :catch->error <id-or-opts> currently undocumented
[opts catch-opts] (impl/signal-catch-opts opts)]
(if catch-opts
`(catch->error! ~catch-opts (impl/signal! ~opts))
(do `(impl/signal! ~opts))))))
(comment
(with-signal (trace! ::my-id (+ 1 2)))
(let [[_ [s1 s2]]
(with-signals
(trace! {:id :id1, :catch->error :id2}
(throw (ex-info "Ex1" {}))))]
[s2]))
#?(:clj
(defmacro spy!
"[form] [level-or-opts form] => run result (value or throw)"
{:doc (impl/signal-docstring :spy!)
:arglists (impl/signal-arglists :spy!)}
[& args]
(let [opts
(impl/signal-opts `spy! (enc/get-source &form &env)
{:kind :spy, :level :info, :msg `impl/default-trace-msg}
:run :level :asc args)
;; :catch->error <id-or-opts> currently undocumented
[opts catch-opts] (impl/signal-catch-opts opts)]
(if catch-opts
`(catch->error! ~catch-opts (impl/signal! ~opts))
(do `(impl/signal! ~opts))))))
(comment (with-signal :force (spy! :info (+ 1 2))))
#?(:clj
(defmacro uncaught->error!
"Uses `uncaught->handler!` so that `error!` will be called for
uncaught JVM errors.
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-arg {:tag 'java.lang.Thread}))]
opts
(impl/signal-opts `uncaught->error! (enc/get-source &form &env)
{:kind :error, :level :error, :msg msg-form}
:error :id :dsc (into ['__throwable-arg] args))]
`(uncaught->handler!
(fn [~'__thread-arg ~'__throwable-arg]
(impl/signal! ~opts))))))
(comment
(macroexpand '(uncaught->error! ::uncaught))
(do
(uncaught->error! ::uncaught)
(enc/threaded :user (/ 1 0))))
#?(:clj #?(:clj
(defn uncaught->handler! (defn uncaught->handler!
@ -361,6 +309,31 @@
(handler thread throwable))))) (handler thread throwable)))))
nil)) nil))
#?(:clj
(let [base-opts
{:kind :error, :level :error,
:msg `["Uncaught Throwable on thread:" (.getName ~(with-meta '__thread-arg {:tag 'java.lang.Thread}))]
:error '__throwable-arg}]
(defmacro uncaught->error!
"Uses `uncaught->handler!` so that `error!` will be called for
uncaught JVM errors.
See `uncaught->handler!` and `error!` for details."
{:arglists (impl/signal-arglists :uncaught->error!)}
([ ] (enc/keep-callsite `(uncaught->error! {})))
([opts-or-id]
(let [opts (merge-or-assoc-opts base-opts &form &env :id opts-or-id)]
`(uncaught->handler!
(fn [~'__thread-arg ~'__throwable-arg]
(impl/signal! ~opts))))))))
(comment
(macroexpand '(uncaught->error! ::uncaught))
(do
(uncaught->error! ::uncaught)
(enc/threaded :user (/ 1 0))))
;;;; ;;;;
(defn dispatch-signal! (defn dispatch-signal!

View file

@ -376,84 +376,84 @@
(defn signal-arglists [macro-id] (defn signal-arglists [macro-id]
(case macro-id (case macro-id
:signal! ; [opts] => allowed? / run result (value or throw) :signal! ; opts => allowed? / unconditional run result (value or throw)
'([{:as opts :keys '([{:as opts-map :keys
[#_defaults #_elide? #_allow? #_expansion-id, ; Undocumented [#_defaults #_elide? #_allow? #_expansion-id, ; Undocumented
elidable? location #_location* inst uid middleware middleware+, elidable? location #_location* inst uid middleware middleware+,
sample-rate kind ns id level when rate-limit rate-limit-by, sample-rate kind ns id level when rate-limit rate-limit-by,
ctx ctx+ parent root trace?, do let data msg error run & kvs]}]) ctx ctx+ parent root trace?, do let data msg error run & kvs]}])
:signal-allowed? :signal-allowed? ; opts => allowed?
'([{:as opts :keys '([{:as opts-map :keys
[#_defaults #_elide? #_allow? #_expansion-id, ; Undocumented [#_defaults #_elide? #_allow? #_expansion-id, ; Undocumented
elidable? location #_location* #_inst #_uid #_middleware #_middleware+, elidable? location #_location* #_inst #_uid #_middleware #_middleware+,
sample-rate kind ns id level when rate-limit rate-limit-by, sample-rate kind ns id level when rate-limit rate-limit-by,
#_ctx #_ctx+ #_parent #_root #_trace?, #_do #_let #_data #_msg #_error #_run #_& #_kvs]}]) #_ctx #_ctx+ #_parent #_root #_trace?, #_do #_let #_data #_msg #_error #_run #_& #_kvs]}])
:event! ; [id] [id level-or-opts] => allowed? :event! ; id + ?level => allowed?
'([id ] '([opts-or-id]
[id level] [id level]
[id [id
{:as opts :keys {:as opts-map :keys
[#_defaults #_elide? #_allow? #_expansion-id, [#_defaults #_elide? #_allow? #_expansion-id,
elidable? location #_location* inst uid middleware middleware+, elidable? location #_location* inst uid middleware middleware+,
sample-rate kind ns id level when rate-limit rate-limit-by, sample-rate kind ns id level when rate-limit rate-limit-by,
ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}]) ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}])
:log! ; [msg] [level-or-opts msg] => allowed? :log! ; ?level + msg => allowed?
'([ msg] '([opts-or-msg]
[level msg] [level msg]
[{:as opts :keys [{:as opts-map :keys
[#_defaults #_elide? #_allow? #_expansion-id, [#_defaults #_elide? #_allow? #_expansion-id,
elidable? location #_location* inst uid middleware middleware+, elidable? location #_location* inst uid middleware middleware+,
sample-rate kind ns id level when rate-limit rate-limit-by, sample-rate kind ns id level when rate-limit rate-limit-by,
ctx ctx+ parent root trace?, do let data msg error #_run & kvs]} ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}
msg]) msg])
:error! ; [error] [id-or-opts error] => given error :trace! ; ?id + run => unconditional run result (value or throw)
'([ error] '([opts-or-run]
[id run]
[{:as opts-map :keys
[#_defaults #_elide? #_allow? #_expansion-id,
elidable? location #_location* inst uid middleware middleware+,
sample-rate kind ns id level when rate-limit rate-limit-by,
ctx ctx+ parent root trace?, do let data msg error run & kvs]}
run])
:spy! ; ?level + run => unconditional run result (value or throw)
'([opts-or-run]
[level run]
[{:as opts-map :keys
[#_defaults #_elide? #_allow? #_expansion-id,
elidable? location #_location* inst uid middleware middleware+,
sample-rate kind ns id level when rate-limit rate-limit-by,
ctx ctx+ parent root trace?, do let data msg error run & kvs]}
run])
:error! ; ?id + error => unconditional given error
'([opts-or-error]
[id error] [id error]
[{:as opts :keys [{:as opts-map :keys
[#_defaults #_elide? #_allow? #_expansion-id, [#_defaults #_elide? #_allow? #_expansion-id,
elidable? location #_location* inst uid middleware middleware+, elidable? location #_location* inst uid middleware middleware+,
sample-rate kind ns id level when rate-limit rate-limit-by, sample-rate kind ns id level when rate-limit rate-limit-by,
ctx ctx+ parent root trace?, do let data msg error #_run & kvs]} ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}
error]) error])
:trace! ; [form] [id-or-opts form] => run result (value or throw) :catch->error! ; ?id + run => unconditional run value or ?catch-val
'([ form] '([opts-or-run]
[id form] [id run]
[{:as opts :keys [{:as opts-map :keys
[#_defaults #_elide? #_allow? #_expansion-id,
elidable? location #_location* inst uid middleware middleware+,
sample-rate kind ns id level when rate-limit rate-limit-by,
ctx ctx+ parent root trace?, do let data msg error run & kvs]}
form])
:spy! ; [form] [level-or-opts form] => run result (value or throw)
'([ form]
[level form]
[{:as opts :keys
[#_defaults #_elide? #_allow? #_expansion-id,
elidable? location #_location* inst uid middleware middleware+,
sample-rate kind ns id level when rate-limit rate-limit-by,
ctx ctx+ parent root trace?, do let data msg error run & kvs]}
form])
:catch->error! ; [form] [id-or-opts form] => run result (value or throw)
'([ form]
[id form]
[{:as opts :keys
[#_defaults #_elide? #_allow? #_expansion-id, rethrow? catch-val, [#_defaults #_elide? #_allow? #_expansion-id, rethrow? catch-val,
elidable? location #_location* inst uid middleware middleware+, elidable? location #_location* inst uid middleware middleware+,
sample-rate kind ns id level when rate-limit rate-limit-by, sample-rate kind ns id level when rate-limit rate-limit-by,
ctx ctx+ parent root trace?, do let data msg error #_run & kvs]} ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}
form]) run])
:uncaught->error! ; [] [id-or-opts] => nil :uncaught->error! ; ?id => nil
'([] '([]
[id] [opts-or-id]
[{:as opts :keys [{:as opts-map :keys
[#_defaults #_elide? #_allow? #_expansion-id, [#_defaults #_elide? #_allow? #_expansion-id,
elidable? location #_location* inst uid middleware middleware+, elidable? location #_location* inst uid middleware middleware+,
sample-rate kind ns id level when rate-limit rate-limit-by, sample-rate kind ns id level when rate-limit rate-limit-by,
@ -461,75 +461,6 @@
(enc/unexpected-arg! macro-id)))) (enc/unexpected-arg! macro-id))))
#?(:clj
(defn signal-opts
"Util to help write common signal wrapper macros."
[context location* defaults main-key extra-key arg-order args]
(enc/cond
:let [context-name (str "`" (name context) "`")
num-args (count args)
bad-args!
(fn [msg data]
(throw
(ex-info (str "Invalid " context-name " args: " msg)
(conj
{:context context
:args args}
data))))]
(not (#{1 2} num-args))
(bad-args! (str "wrong number of args (" num-args ")")
{:actual num-args, :expected #{1 2}})
:let [[main-arg extra-arg]
(case arg-order
:dsc args ; [main ...]
:asc (reverse args) ; [... main]
(enc/unexpected-arg!
arg-order))
extra-arg? (= num-args 2)
extra-opts? (and extra-arg? (map? extra-arg))]
:do
(enc/cond
(and (map? main-arg) (not extra-arg?))
(bad-args! "single map arg is USUALLY a mistake, so isn't allowed. Please use 2 arg arity instead, or `signal!`." {})
(and extra-opts? (contains? extra-arg main-key))
(bad-args! (str "given opts should not contain `" main-key "`.") {}))
:let [base (merge defaults {:location* location*, main-key main-arg})]
extra-opts? (merge base extra-arg)
extra-arg? (merge base {extra-key extra-arg})
:else base)))
(comment (signal-opts `foo! :loc* {:level :info} :id :level :dsc [::my-id {:level :warn}]))
#?(:clj
(defn signal-catch-opts
"For use within `trace!` and `spy!`, etc."
[main-opts]
(let [catch-id-or-opts (get main-opts :catch->error)
main-opts (dissoc main-opts :catch->error)
catch-opts
(when catch-id-or-opts
(let [base ; Inherit some opts from main
(enc/assoc-some {}
:location* (get main-opts :location*)
:id (get main-opts :id))]
(cond
(true? catch-id-or-opts) (do base)
(map? catch-id-or-opts) (conj base catch-id-or-opts)
:else (conj base {:id catch-id-or-opts}))))]
[main-opts catch-opts])))
(comment
(signal-catch-opts {:id :main-id, :catch->error true})
(signal-catch-opts {:id :main-id, :catch->error :error-id})
(signal-catch-opts {:id :main-id, :catch->error {:id :error-id}}))
;;;; Signal macro ;;;; Signal macro
(deftype RunResult [value error ^long run-nsecs] (deftype RunResult [value error ^long run-nsecs]

View file

@ -74,8 +74,8 @@
(comment (comment
(macroexpand '(trace "foo")) (macroexpand '(trace "foo"))
(tel/with-signal :force-msg (trace "foo")) (tel/with-signal (trace "foo"))
(tel/with-signal :force-msg (infof "Hello %s" "world"))) (tel/with-signal (infof "Hello %s" "world")))
#?(:clj #?(:clj
(do (do
@ -103,24 +103,27 @@
([ form] (enc/keep-callsite `(spy! :debug nil ~form))) ([ form] (enc/keep-callsite `(spy! :debug nil ~form)))
([level form] (enc/keep-callsite `(spy! ~level nil ~form))) ([level form] (enc/keep-callsite `(spy! ~level nil ~form)))
([level form-name form] ([level form-name form]
(let [msg (let [location* (enc/get-source &form &env)
(if-not form-name msg
`impl/default-trace-msg (if form-name
`(fn [_form# value# error# nsecs#] `(fn [_form# value# error# nsecs#] (impl/default-trace-msg ~form-name value# error# nsecs#))
(impl/default-trace-msg ~form-name value# error# nsecs#)))] `(fn [_form# value# error# nsecs#] (impl/default-trace-msg '~form value# error# nsecs#)))]
(enc/keep-callsite
`(tel/spy! `(tel/spy!
{:kind :spy {:location* ~location*
:level ~level
:id shim-id :id shim-id
:msg ~msg :level ~level
:catch->error true} :msg ~msg}
(tel/catch->error!
{:location* ~location*
:id shim-id}
~form)))))) ~form))))))
(comment (comment
(select-keys (tel/with-signal :force-msg (spy! :info "my-form-name" (+ 1 2))) [:level :msg_]) (:level (tel/with-signal (spy! (/ 1 0))))
(select-keys (tel/with-signal :force-msg (spy! :info "my-form-name" (throw (Exception. "Ex")))) [:level #_:msg_])) (select-keys (tel/with-signal (spy! :info #_"my-form-name" (+ 1 2))) [:level :msg_])
(select-keys (tel/with-signal (spy! :info #_"my-form-name" (throw (Exception. "Ex")))) [:level :msg_]))
#?(:clj (defmacro log-errors "Prefer `telemere/catch->error!`." [& body] (enc/keep-callsite `(tel/catch->error! {:id shim-id, :catch-val nil} (do ~@body))))) #?(:clj (defmacro log-errors "Prefer `telemere/catch->error!`." [& body] (enc/keep-callsite `(tel/catch->error! {:id shim-id, :catch-val nil} (do ~@body)))))
#?(:clj (defmacro log-and-rethrow-errors "Prefer `telemere/catch->error!`." [& body] (enc/keep-callsite `(tel/catch->error! {:id shim-id} (do ~@body))))) #?(:clj (defmacro log-and-rethrow-errors "Prefer `telemere/catch->error!`." [& body] (enc/keep-callsite `(tel/catch->error! {:id shim-id} (do ~@body)))))

View file

@ -568,42 +568,61 @@
;;;; ;;;;
(deftest _common-signals (deftest _common-signals
[#?(:clj [(testing "event!" ; id + ?level => allowed?
(testing "signal-opts"
[(is (= (impl/signal-opts `foo! :loc* {:level :info} :id :level :dsc [::my-id ]) {:level :info, :id ::my-id, :location* :loc*}))
(is (= (impl/signal-opts `foo! :loc* {:level :info} :id :level :dsc [::my-id :warn ]) {:level :warn, :id ::my-id, :location* :loc*}))
(is (= (impl/signal-opts `foo! :loc* {:level :info} :id :level :dsc [::my-id {:level :warn}]) {:level :warn, :id ::my-id, :location* :loc*}))
(is (= (impl/signal-opts `foo! :loc* {:level :info} :id :level :asc [ ::my-id]) {:level :info, :id ::my-id, :location* :loc*}))
(is (= (impl/signal-opts `foo! :loc* {:level :info} :id :level :asc [:warn ::my-id]) {:level :warn, :id ::my-id, :location* :loc*}))
(is (= (impl/signal-opts `foo! :loc* {:level :info} :id :level :asc [{:level :warn} ::my-id]) {:level :warn, :id ::my-id, :location* :loc*}))
(is (= (impl/signal-catch-opts {:id :main-id, :location* {:ns "ns"}, :catch->error true}) [{:id :main-id, :location* {:ns "ns"}} {:location* {:ns "ns"}, :id :main-id}]))
(is (= (impl/signal-catch-opts {:id :main-id, :location* {:ns "ns"}, :catch->error :error-id}) [{:id :main-id, :location* {:ns "ns"}} {:location* {:ns "ns"}, :id :error-id}]))
(is (= (impl/signal-catch-opts {:id :main-id, :location* {:ns "ns"}, :catch->error {:id :error-id}}) [{:id :main-id, :location* {:ns "ns"}} {:location* {:ns "ns"}, :id :error-id}]))
(is (throws? :ex-info "Invalid `foo!` args: single map arg is USUALLY a mistake" (impl/signal-opts `foo! :loc* {:level :info} :id :level :dsc [{:msg "msg"}])))
(is (throws? :ex-info "Invalid `foo!` args: given opts should not contain `:id`" (impl/signal-opts `foo! :loc* {:level :info} :id :level :dsc [:my-id1 {:id ::my-id2}])))]))
(testing "event!" ; id + ?level => allowed?
[(let [[[rv] [sv]] (with-sigs (tel/event! :id1 )) ] [(is (= rv true)) (is (sm? sv {:kind :event, :line :submap/some, :level :info, :id :id1}))]) [(let [[[rv] [sv]] (with-sigs (tel/event! :id1 )) ] [(is (= rv true)) (is (sm? sv {:kind :event, :line :submap/some, :level :info, :id :id1}))])
(let [[[rv] [sv]] (with-sigs (tel/event! :id1 :warn)) ] [(is (= rv true)) (is (sm? sv {:kind :event, :line :submap/some, :level :warn, :id :id1}))]) (let [[[rv] [sv]] (with-sigs (tel/event! :id1 :warn)) ] [(is (= rv true)) (is (sm? sv {:kind :event, :line :submap/some, :level :warn, :id :id1}))])
(let [[[rv] [sv]] (with-sigs (tel/event! :id1 {:level :warn}))] [(is (= rv true)) (is (sm? sv {:kind :event, :line :submap/some, :level :warn, :id :id1}))]) (let [[[rv] [sv]] (with-sigs (tel/event! :id1 {:level :warn}))] [(is (= rv true)) (is (sm? sv {:kind :event, :line :submap/some, :level :warn, :id :id1}))])
(let [[[rv] [sv]] (with-sigs (tel/event! {:id :id1, :level :warn}))] [(is (= rv true)) (is (sm? sv {:kind :event, :line :submap/some, :level :warn, :id :id1}))])
(let [[[rv] [sv]] (with-sigs (tel/event! :id1 {:allow? false})) ] [(is (= rv nil)) (is (nil? sv))])]) (let [[[rv] [sv]] (with-sigs (tel/event! :id1 {:allow? false})) ] [(is (= rv nil)) (is (nil? sv))])])
(testing "error!" ; error + ?id => error (testing "log!" ; ?level + msg => allowed?
[(let [[[rv] [sv]] (with-sigs (tel/error! ex1))] [(is (ex1? rv)) (is (sm? sv {:kind :error, :line :submap/some, :level :error, :error pex1?, :id nil}))])
(let [[[rv] [sv]] (with-sigs (tel/error! :id1 ex1))] [(is (ex1? rv)) (is (sm? sv {:kind :error, :line :submap/some, :level :error, :error pex1?, :id :id1}))])
(let [[[rv] [sv]] (with-sigs (tel/error! {:id :id1} ex1))] [(is (ex1? rv)) (is (sm? sv {:kind :error, :line :submap/some, :level :error, :error pex1?, :id :id1}))])
(let [[[rv] [sv]] (with-sigs (tel/error! {:allow? false} ex1))] [(is (ex1? rv)) (is (nil? sv))])])
(testing "log!" ; msg + ?level => allowed?
[(let [[[rv] [sv]] (with-sigs (tel/log! "msg")) ] [(is (= rv true)) (is (sm? sv {:kind :log, :line :submap/some, :msg_ "msg", :level :info}))]) [(let [[[rv] [sv]] (with-sigs (tel/log! "msg")) ] [(is (= rv true)) (is (sm? sv {:kind :log, :line :submap/some, :msg_ "msg", :level :info}))])
(let [[[rv] [sv]] (with-sigs (tel/log! :warn "msg")) ] [(is (= rv true)) (is (sm? sv {:kind :log, :line :submap/some, :msg_ "msg", :level :warn}))]) (let [[[rv] [sv]] (with-sigs (tel/log! :warn "msg")) ] [(is (= rv true)) (is (sm? sv {:kind :log, :line :submap/some, :msg_ "msg", :level :warn}))])
(let [[[rv] [sv]] (with-sigs (tel/log! {:level :warn} "msg")) ] [(is (= rv true)) (is (sm? sv {:kind :log, :line :submap/some, :msg_ "msg", :level :warn}))]) (let [[[rv] [sv]] (with-sigs (tel/log! {:level :warn} "msg")) ] [(is (= rv true)) (is (sm? sv {:kind :log, :line :submap/some, :msg_ "msg", :level :warn}))])
(let [[[rv] [sv]] (with-sigs (tel/log! {:level :warn, :msg "msg"}))] [(is (= rv true)) (is (sm? sv {:kind :log, :line :submap/some, :msg_ "msg", :level :warn}))])
(let [[[rv] [sv]] (with-sigs (tel/log! {:allow? false} "msg")) ] [(is (= rv nil)) (is (nil? sv))])]) (let [[[rv] [sv]] (with-sigs (tel/log! {:allow? false} "msg")) ] [(is (= rv nil)) (is (nil? sv))])])
(testing "catch->error!" ; form + ?id => run value or ?return (testing "trace!" ; ?id + run => unconditional run result (value or throw)
[(let [[[rv] [sv]] (with-sigs (tel/trace! (+ 1 2))) ] [(is (= rv 3)) (is (sm? sv {:kind :trace, :line :submap/some, :level :info, :id nil, :msg_ "(+ 1 2) => 3"}))])
(let [[[rv] [sv]] (with-sigs (tel/trace! {:msg nil} (+ 1 2))) ] [(is (= rv 3)) (is (sm? sv {:kind :trace, :line :submap/some, :level :info, :id nil, :msg_ nil}))])
(let [[[rv] [sv]] (with-sigs (tel/trace! :id1 (+ 1 2))) ] [(is (= rv 3)) (is (sm? sv {:kind :trace, :line :submap/some, :level :info, :id :id1}))])
(let [[[rv] [sv]] (with-sigs (tel/trace! {:id :id1} (+ 1 2))) ] [(is (= rv 3)) (is (sm? sv {:kind :trace, :line :submap/some, :level :info, :id :id1}))])
(let [[[rv] [sv]] (with-sigs (tel/trace! {:id :id1, :run (+ 1 2)}))] [(is (= rv 3)) (is (sm? sv {:kind :trace, :line :submap/some, :level :info, :id :id1}))])
(let [[[_ re] [sv]] (with-sigs (tel/trace! :id1 (ex1!))) ] [(is (ex1? re)) (is (sm? sv {:kind :trace, :line :submap/some, :level :info, :id :id1, :error pex1?,
:msg_ #?(:clj "(ex1!) !> clojure.lang.ExceptionInfo"
:cljs "(ex1!) !> cljs.core/ExceptionInfo")}))])
(let [[[rv] [sv]] (with-sigs (tel/trace! {:allow? false} (+ 1 2))) ] [(is (= rv 3)) (is (nil? sv))])
(testing ":run-form" ; Undocumented, experimental
[(is (sm? (with-sig (tel/trace! :non-list)) {:run-form :non-list}))
(is (sm? (with-sig (tel/trace! (+ 1 2 3 4))) {:run-form '(+ 1 2 3 4)}))
(is (sm? (with-sig (tel/trace! (+ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16))) {:run-form '(+ ...)}))
(is (sm? (with-sig (tel/trace! {:run-form my-run-form} (+ 1 2 3 4))) {:run-form 'my-run-form :kvs nil}))])
(testing ":run-val" ; Undocumented, experimental
[(is (sm? (with-sig (tel/trace! (+ 2 2))) {:run-val 4, :msg_ "(+ 2 2) => 4"}))
(is (sm? (with-sig (tel/trace! {:run-val "custom"} (+ 2 2))) {:run-val "custom", :msg_ "(+ 2 2) => custom", :kvs nil}))])])
(testing "spy" ; ?level + run => unconditional run result (value or throw)
[(let [[[rv] [sv]] (with-sigs (tel/spy! (+ 1 2))) ] [(is (= rv 3)) (is (sm? sv {:kind :spy, :line :submap/some, :level :info, :msg_ "(+ 1 2) => 3"}))])
(let [[[rv] [sv]] (with-sigs (tel/spy! {:msg nil} (+ 1 2))) ] [(is (= rv 3)) (is (sm? sv {:kind :spy, :line :submap/some, :level :info, :msg_ nil}))])
(let [[[rv] [sv]] (with-sigs (tel/spy! :warn (+ 1 2))) ] [(is (= rv 3)) (is (sm? sv {:kind :spy, :line :submap/some, :level :warn}))])
(let [[[rv] [sv]] (with-sigs (tel/spy! {:level :warn} (+ 1 2))) ] [(is (= rv 3)) (is (sm? sv {:kind :spy, :line :submap/some, :level :warn}))])
(let [[[rv] [sv]] (with-sigs (tel/spy! {:level :warn, :run (+ 1 2)}))] [(is (= rv 3)) (is (sm? sv {:kind :spy, :line :submap/some, :level :warn}))])
(let [[[_ re] [sv]] (with-sigs (tel/spy! :warn (ex1!)))] [(is (ex1? re)) (is (sm? sv {:kind :spy, :line :submap/some, :level :warn, :error pex1?,
:msg_ #?(:clj "(ex1!) !> clojure.lang.ExceptionInfo"
:cljs "(ex1!) !> cljs.core/ExceptionInfo")}))])
(let [[[rv] [sv]] (with-sigs (tel/spy! {:allow? false} (+ 1 2))) ] [(is (= rv 3)) (is (nil? sv))])])
(testing "error!" ; ?id + error => unconditional given error
[(let [[[rv] [sv]] (with-sigs (tel/error! ex1)) ] [(is (ex1? rv)) (is (sm? sv {:kind :error, :line :submap/some, :level :error, :error pex1?, :id nil}))])
(let [[[rv] [sv]] (with-sigs (tel/error! :id1 ex1)) ] [(is (ex1? rv)) (is (sm? sv {:kind :error, :line :submap/some, :level :error, :error pex1?, :id :id1}))])
(let [[[rv] [sv]] (with-sigs (tel/error! {:id :id1} ex1)) ] [(is (ex1? rv)) (is (sm? sv {:kind :error, :line :submap/some, :level :error, :error pex1?, :id :id1}))])
(let [[[rv] [sv]] (with-sigs (tel/error! {:id :id1, :error ex1}))] [(is (ex1? rv)) (is (sm? sv {:kind :error, :line :submap/some, :level :error, :error pex1?, :id :id1}))])
(let [[[rv] [sv]] (with-sigs (tel/error! {:allow? false} ex1)) ] [(is (ex1? rv)) (is (nil? sv))])
(let [c (enc/counter)] (tel/error! (do (c) ex1)) (is (= @c 1) "Error form evaluated exactly once"))])
(testing "catch->error!" ; ?id + run => unconditional run value or ?return
[(let [[[rv re] [sv]] (with-sigs (tel/catch->error! (+ 1 2)))] [(is (= rv 3)) (is (nil? sv))]) [(let [[[rv re] [sv]] (with-sigs (tel/catch->error! (+ 1 2)))] [(is (= rv 3)) (is (nil? sv))])
(let [[[rv re] [sv]] (with-sigs (tel/catch->error! (ex1!)))] [(is (ex1? re)) (is (sm? sv {:kind :error, :line :submap/some, :level :error, :error pex1?, :id nil}))]) (let [[[rv re] [sv]] (with-sigs (tel/catch->error! (ex1!)))] [(is (ex1? re)) (is (sm? sv {:kind :error, :line :submap/some, :level :error, :error pex1?, :id nil}))])
(let [[[rv re] [sv]] (with-sigs (tel/catch->error! :id1 (ex1!)))] [(is (ex1? re)) (is (sm? sv {:kind :error, :line :submap/some, :level :error, :error pex1?, :id :id1}))]) (let [[[rv re] [sv]] (with-sigs (tel/catch->error! :id1 (ex1!)))] [(is (ex1? re)) (is (sm? sv {:kind :error, :line :submap/some, :level :error, :error pex1?, :id :id1}))])
@ -619,46 +638,6 @@
:data {:my-err my-err}} (ex1!)))] :data {:my-err my-err}} (ex1!)))]
[(is (= rv nil)) (is (sm? sv {:kind :error, :data {:my-err pex1?}}))])]) [(is (= rv nil)) (is (sm? sv {:kind :error, :data {:my-err pex1?}}))])])
(testing "trace!" ; run + ?id => run result (value or throw)
[(let [[[rv] [sv]] (with-sigs (tel/trace! (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :trace, :line :submap/some, :level :info, :id nil, :msg_ "(+ 1 2) => 3"}))])
(let [[[rv] [sv]] (with-sigs (tel/trace! {:msg nil} (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :trace, :line :submap/some, :level :info, :id nil, :msg_ nil}))])
(let [[[rv] [sv]] (with-sigs (tel/trace! :id1 (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :trace, :line :submap/some, :level :info, :id :id1}))])
(let [[[rv] [sv]] (with-sigs (tel/trace! {:id :id1} (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :trace, :line :submap/some, :level :info, :id :id1}))])
(let [[[_ re] [sv]] (with-sigs (tel/trace! :id1 (ex1!)))] [(is (ex1? re)) (is (sm? sv {:kind :trace, :line :submap/some, :level :info, :id :id1, :error pex1?,
:msg_ #?(:clj "(ex1!) !> clojure.lang.ExceptionInfo"
:cljs "(ex1!) !> cljs.core/ExceptionInfo")}))])
(let [[[rv] [sv]] (with-sigs (tel/trace! {:allow? false} (+ 1 2)))] [(is (= rv 3)) (is (nil? sv))])
(let [[_ [sv1 sv2]]
(with-sigs (tel/trace! {:id :id1, :catch->error :id2} (ex1!)))]
[(is (sm? sv1 {:kind :trace, :line :submap/some, :level :info, :id :id1}))
(is (sm? sv2 {:kind :error, :line :submap/some, :level :error, :id :id2}))
(is (= (:location sv1) (:location sv2)) "Error inherits exact same location")])
(testing ":run-form" ; Undocumented, experimental
[(is (sm? (with-sig (tel/trace! :non-list)) {:run-form :non-list}))
(is (sm? (with-sig (tel/trace! (+ 1 2 3 4))) {:run-form '(+ 1 2 3 4)}))
(is (sm? (with-sig (tel/trace! (+ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16))) {:run-form '(+ ...)}))
(is (sm? (with-sig (tel/trace! {:run-form my-run-form} (+ 1 2 3 4))) {:run-form 'my-run-form :kvs nil}))])
(testing ":run-val" ; Undocumented, experimental
[(is (sm? (with-sig (tel/trace! (+ 2 2))) {:run-val 4, :msg_ "(+ 2 2) => 4"}))
(is (sm? (with-sig (tel/trace! {:run-val "custom"} (+ 2 2))) {:run-val "custom", :msg_ "(+ 2 2) => custom", :kvs nil}))])])
(testing "spy" ; run + ?level => run result (value or throw)
[(let [[[rv] [sv]] (with-sigs (tel/spy! (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :spy, :line :submap/some, :level :info, :msg_ "(+ 1 2) => 3"}))])
(let [[[rv] [sv]] (with-sigs (tel/spy! {:msg nil} (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :spy, :line :submap/some, :level :info, :msg_ nil}))])
(let [[[rv] [sv]] (with-sigs (tel/spy! :warn (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :spy, :line :submap/some, :level :warn}))])
(let [[[rv] [sv]] (with-sigs (tel/spy! {:level :warn} (+ 1 2)))] [(is (= rv 3)) (is (sm? sv {:kind :spy, :line :submap/some, :level :warn}))])
(let [[[_ re] [sv]] (with-sigs (tel/spy! :warn (ex1!)))] [(is (ex1? re)) (is (sm? sv {:kind :spy, :line :submap/some, :level :warn, :error pex1?,
:msg_ #?(:clj "(ex1!) !> clojure.lang.ExceptionInfo"
:cljs "(ex1!) !> cljs.core/ExceptionInfo")}))])
(let [[[rv] [sv]] (with-sigs (tel/spy! {:allow? false} (+ 1 2)))] [(is (= rv 3)) (is (nil? sv))])
(let [[_ [sv1 sv2]]
(with-sigs (tel/spy! {:id :id1, :catch->error :id2} (ex1!)))]
[(is (sm? sv1 {:kind :spy, :line :submap/some, :level :info, :id :id1}))
(is (sm? sv2 {:kind :error, :line :submap/some, :level :error, :id :id2}))
(is (= (:location sv1) (:location sv2)) "Error inherits exact same location")])])
#?(:clj #?(:clj
(testing "uncaught->error!" (testing "uncaught->error!"
(let [sv_ (atom ::nx)] (let [sv_ (atom ::nx)]
@ -770,8 +749,11 @@
(is (sm? (with-sig (timbre/info ex1 "x1" "x2")) {:kind :log, :level :info, :error pex1?, :msg_ "x1 x2", :data {:vargs ["x1" "x2"]}}) "First-arg error") (is (sm? (with-sig (timbre/info ex1 "x1" "x2")) {:kind :log, :level :info, :error pex1?, :msg_ "x1 x2", :data {:vargs ["x1" "x2"]}}) "First-arg error")
(is (sm? (with-sig (timbre/spy! :info "my-name" (+ 1 2))) {:kind :spy, :level :info, :id timbre/shim-id, :msg_ "my-name => 3", :ns pstr?})) (is (sm? (with-sig (timbre/spy! :info "my-name" (+ 1 2))) {:kind :spy, :level :info, :id timbre/shim-id, :msg_ "my-name => 3", :ns pstr?}))
(is (sm? (with-sig (timbre/spy! (+ 1 2))) {:kind :spy, :level :debug, :id timbre/shim-id, :msg_ "(+ 1 2) => 3", :ns pstr?})) (is (sm? (tel/with-min-level :debug (with-sig (timbre/spy! (+ 1 2)))) {:kind :spy, :level :debug, :id timbre/shim-id, :msg_ "(+ 1 2) => 3", :ns pstr?}))
(is (sm? (with-sig (timbre/spy! (ex1!))) {:kind :error, :level :error, :id timbre/shim-id, :msg_ nil, :error pex1?, :ns pstr?}))
(let [[_ [sv1 sv2]] (tel/with-min-level :debug (with-sigs (timbre/spy! (ex1!))))]
[(is (sm? sv1 {:kind :error, :level :error, :id timbre/shim-id, :msg_ nil, :error pex1?, :ns pstr?}))
(is (sm? sv2 {:kind :spy, :level :debug, :id timbre/shim-id, :msg_ pstr? :error pex1?, :ns pstr?}))])
(let [[[rv re] [sv]] (with-sigs (timbre/log-errors (ex1!)))] [(is (nil? re)) (is (sm? sv {:kind :error, :level :error, :error pex1?, :id timbre/shim-id}))]) (let [[[rv re] [sv]] (with-sigs (timbre/log-errors (ex1!)))] [(is (nil? re)) (is (sm? sv {:kind :error, :level :error, :error pex1?, :id timbre/shim-id}))])
(let [[[rv re] [sv]] (with-sigs (timbre/log-and-rethrow-errors (ex1!)))] [(is (ex1? re)) (is (sm? sv {:kind :error, :level :error, :error pex1?, :id timbre/shim-id}))])]) (let [[[rv re] [sv]] (with-sigs (timbre/log-and-rethrow-errors (ex1!)))] [(is (ex1? re)) (is (sm? sv {:kind :error, :level :error, :error pex1?, :id timbre/shim-id}))])])