[new] Add base-opts arg to impl signal creator

This commit is contained in:
Peter Taoussanis 2025-03-03 09:17:47 +01:00
parent 46e82f0816
commit e32ed8deb5
2 changed files with 204 additions and 200 deletions

View file

@ -242,7 +242,7 @@
(truss/keep-callsite (truss/keep-callsite
`(impl/signal! ~(args->opts args))))) `(impl/signal! ~(args->opts args)))))
(comment (:coords (with-signal (signal!)))) (comment (:coords (macroexpand '(with-signal (signal!)))))
#?(:clj #?(:clj
(defn- merge-or-assoc-opts [m macro-form k v] (defn- merge-or-assoc-opts [m macro-form k v]

View file

@ -511,9 +511,11 @@
(defmacro signal-allowed? (defmacro signal-allowed?
"Returns true iff signal with given opts would meet filtering conditions. "Returns true iff signal with given opts would meet filtering conditions.
Wrapped for public API." Wrapped for public API."
[opts] ([ opts] (truss/keep-callsite `(signal-allowed? nil ~opts)))
(valid-opts! opts) ([base-opts opts]
(let [opts (merge {:kind :generic, :level :info} opts) (valid-opts! (or base-opts {}))
(valid-opts! (or opts {}))
(let [opts (merge {:kind :generic, :level :info} base-opts opts)
{:keys [#_callsite-id elide? allow?]} {:keys [#_callsite-id elide? allow?]}
(sigs/filter-call (sigs/filter-call
{:cljs? (boolean (:ns &env)) {:cljs? (boolean (:ns &env))
@ -523,62 +525,64 @@
(assoc opts (assoc opts
:ns (auto-> (get opts :ns :auto) (str *ns*))))] :ns (auto-> (get opts :ns :auto) (str *ns*))))]
(if elide? false `(if ~allow? true false))))) (if elide? false `(if ~allow? true false))))))
(comment (macroexpand '(signal-allowed? {:level :info}))) (comment (macroexpand '(signal-allowed? {:level :info})))
#?(:clj #?(:clj
(defmacro signal! (defmacro signal!
"Generic low-level signal creator. Wrapped for public API." "Generic low-level signal creator. Wrapped for public API."
[opts] ([ opts] (truss/keep-callsite `(signal!? nil ~opts)))
(valid-opts! opts) ([base-opts opts]
(let [cljs? (boolean (:ns &env)) (valid-opts! (or base-opts {}))
clj? (not cljs?) (valid-opts! (or opts {}))
(let [cljs? (boolean (:ns &env))
clj? (not cljs?)
opts (merge {:kind :generic, :level :info} opts) opts (merge {:kind :generic, :level :info} base-opts opts)
{run-form :run} opts {run-form :run} opts
ns-form* (get opts :ns :auto) ns-form* (get opts :ns :auto)
ns-form (auto-> ns-form* (str *ns*)) ns-form (auto-> ns-form* (str *ns*))
show-run-val (get opts :run-val '_run-val) show-run-val (get opts :run-val '_run-val)
show-run-form show-run-form
(when run-form (when run-form
(get opts :run-form (get opts :run-form
(if (and (if (and
(enc/list-form? run-form) (enc/list-form? run-form)
(> (count run-form) 1) (> (count run-form) 1)
(> (count (str run-form)) 32)) (> (count (str run-form)) 32))
(list (first run-form) '...) (list (first run-form) '...)
(do run-form)))) (do run-form))))
{:keys [#_callsite-id elide? allow?]} {:keys [#_callsite-id elide? allow?]}
(sigs/filter-call (sigs/filter-call
{:cljs? cljs? {:cljs? cljs?
:sf-arity 4 :sf-arity 4
:ct-call-filter ct-call-filter :ct-call-filter ct-call-filter
:*rt-call-filter* `*rt-call-filter*} :*rt-call-filter* `*rt-call-filter*}
(assoc opts (assoc opts
:ns ns-form :ns ns-form
:local-forms :local-forms
{:kind '__kind {:kind '__kind
:ns '__ns :ns '__ns
:id '__id :id '__id
:level '__level}))] :level '__level}))]
(if elide? (if elide?
run-form run-form
(let [coords (let [coords
(get opts :coords (get opts :coords
(when (= ns-form* :auto) (when (= ns-form* :auto)
;; Auto coords iff auto ns ;; Auto coords iff auto ns
(truss/callsite-coords &form))) (truss/callsite-coords &form)))
{inst-form :inst {inst-form :inst
level-form :level level-form :level
kind-form :kind kind-form :kind
id-form :id} opts id-form :id} opts
trace? (get opts :trace? (boolean run-form)) trace? (get opts :trace? (boolean run-form))
_ _
@ -588,185 +592,185 @@
:context `signal! :context `signal!
:msg "Expected constant (compile-time) `:trace?` boolean"})) :msg "Expected constant (compile-time) `:trace?` boolean"}))
thread-form (when clj? `(enc/thread-info)) thread-form (when clj? `(enc/thread-info))
inst-form (get opts :inst :auto) inst-form (get opts :inst :auto)
inst-form (auto-> inst-form `(enc/now-inst*)) inst-form (auto-> inst-form `(enc/now-inst*))
parent-form (get opts :parent `*trace-parent*) parent-form (get opts :parent `*trace-parent*)
root-form0 (get opts :root `*trace-root*) root-form0 (get opts :root `*trace-root*)
uid-form (get opts :uid (when trace? :auto)) uid-form (get opts :uid (when trace? :auto))
signal-delay-form signal-delay-form
(let [{do-form :do (let [{do-form :do
let-form :let let-form :let
msg-form :msg msg-form :msg
data-form :data data-form :data
error-form :error error-form :error
sample-rate-form :sample-rate} opts sample-rate-form :sample-rate} opts
let-form (or let-form '[]) let-form (or let-form '[])
msg-form (parse-msg-form msg-form) msg-form (parse-msg-form msg-form)
ctx-form ctx-form
(if-let [ctx+ (get opts :ctx+)] (if-let [ctx+ (get opts :ctx+)]
`(taoensso.encore.signals/update-ctx taoensso.telemere/*ctx* ~ctx+) `(taoensso.encore.signals/update-ctx taoensso.telemere/*ctx* ~ctx+)
(get opts :ctx `taoensso.telemere/*ctx*)) (get opts :ctx `taoensso.telemere/*ctx*))
middleware-form middleware-form
(if-let [middleware+ (get opts :middleware+)] (if-let [middleware+ (get opts :middleware+)]
`(taoensso.encore/comp-middleware taoensso.telemere/*middleware* ~middleware+) `(taoensso.encore/comp-middleware taoensso.telemere/*middleware* ~middleware+)
(get opts :middleware `taoensso.telemere/*middleware*)) (get opts :middleware `taoensso.telemere/*middleware*))
kvs-form kvs-form
(not-empty (not-empty
(dissoc opts (dissoc opts
:elidable? :coords :inst :uid :middleware :middleware+, :elidable? :coords :inst :uid :middleware :middleware+,
:sample-rate :ns :kind :id :level :filter :when #_:rate-limit #_:rate-limit-by, :sample-rate :ns :kind :id :level :filter :when #_:rate-limit #_:rate-limit-by,
:ctx :ctx+ :parent #_:trace?, :do :let :data :msg :error, :ctx :ctx+ :parent #_:trace?, :do :let :data :msg :error,
:run :run-form :run-val, :elide? :allow? #_:callsite-id :otel/context)) :run :run-form :run-val, :elide? :allow? #_:callsite-id :otel/context))
_ ; Compile-time validation _ ; Compile-time validation
(do (do
(when (and run-form error-form) ; Ambiguous source of error (when (and run-form error-form) ; Ambiguous source of error
(truss/ex-info! "Signals cannot have both `:run` and `:error` opts at the same time" (truss/ex-info! "Signals cannot have both `:run` and `:error` opts at the same time"
{:run-form run-form {:run-form run-form
:error-form error-form :error-form error-form
:ns ns-form :ns ns-form
:coords coords :coords coords
:other-opts (dissoc opts :run :error)})) :other-opts (dissoc opts :run :error)}))
(when-let [e (find opts :msg_)] ; Common typo/confusion (when-let [e (find opts :msg_)] ; Common typo/confusion
(truss/ex-info! "Signals cannot have `:msg_` opt (did you mean `:msg`?))" (truss/ex-info! "Signals cannot have `:msg_` opt (did you mean `:msg`?))"
{:msg_ (truss/typed-val (val e))}))) {:msg_ (truss/typed-val (val e))})))
signal-form signal-form
(let [record-form (let [record-form
(let [clause [(if run-form :run :no-run) (if clj? :clj :cljs)]] (let [clause [(if run-form :run :no-run) (if clj? :clj :cljs)]]
(case clause (case clause
[:run :clj ] `(Signal. 1 ~'__inst ~'__uid, ~'__ns ~coords (enc/host-info) ~'__thread ~'__otel-context1, ~sample-rate-form, ~'__kind ~'__id ~'__level, ~ctx-form ~parent-form ~'__root1, ~data-form ~kvs-form ~'_msg_, ~'_run-err '~show-run-form ~show-run-val ~'_end-inst ~'_run-nsecs) [:run :clj ] `(Signal. 1 ~'__inst ~'__uid, ~'__ns ~coords (enc/host-info) ~'__thread ~'__otel-context1, ~sample-rate-form, ~'__kind ~'__id ~'__level, ~ctx-form ~parent-form ~'__root1, ~data-form ~kvs-form ~'_msg_, ~'_run-err '~show-run-form ~show-run-val ~'_end-inst ~'_run-nsecs)
[:run :cljs] `(Signal. 1 ~'__inst ~'__uid, ~'__ns ~coords ~sample-rate-form, ~'__kind ~'__id ~'__level, ~ctx-form ~parent-form ~'__root1, ~data-form ~kvs-form ~'_msg_, ~'_run-err '~show-run-form ~show-run-val ~'_end-inst ~'_run-nsecs) [:run :cljs] `(Signal. 1 ~'__inst ~'__uid, ~'__ns ~coords ~sample-rate-form, ~'__kind ~'__id ~'__level, ~ctx-form ~parent-form ~'__root1, ~data-form ~kvs-form ~'_msg_, ~'_run-err '~show-run-form ~show-run-val ~'_end-inst ~'_run-nsecs)
[:no-run :clj ] `(Signal. 1 ~'__inst ~'__uid, ~'__ns ~coords (enc/host-info) ~'__thread ~'__otel-context1, ~sample-rate-form, ~'__kind ~'__id ~'__level, ~ctx-form ~parent-form ~'__root1, ~data-form ~kvs-form ~msg-form, ~error-form nil nil nil nil) [:no-run :clj ] `(Signal. 1 ~'__inst ~'__uid, ~'__ns ~coords (enc/host-info) ~'__thread ~'__otel-context1, ~sample-rate-form, ~'__kind ~'__id ~'__level, ~ctx-form ~parent-form ~'__root1, ~data-form ~kvs-form ~msg-form, ~error-form nil nil nil nil)
[:no-run :cljs] `(Signal. 1 ~'__inst ~'__uid, ~'__ns ~coords ~sample-rate-form, ~'__kind ~'__id ~'__level, ~ctx-form ~parent-form ~'__root1, ~data-form ~kvs-form ~msg-form, ~error-form nil nil nil nil) [:no-run :cljs] `(Signal. 1 ~'__inst ~'__uid, ~'__ns ~coords ~sample-rate-form, ~'__kind ~'__id ~'__level, ~ctx-form ~parent-form ~'__root1, ~data-form ~kvs-form ~msg-form, ~error-form nil nil nil nil)
(truss/unexpected-arg! clause {:context :signal-constructor-args}))) (truss/unexpected-arg! clause {:context :signal-constructor-args})))
record-form record-form
(if-not run-form (if-not run-form
record-form record-form
`(let [~(with-meta '_run-result {:tag `RunResult}) ~'__run-result `(let [~(with-meta '_run-result {:tag `RunResult}) ~'__run-result
~'_run-nsecs (.-run-nsecs ~'_run-result) ~'_run-nsecs (.-run-nsecs ~'_run-result)
~'_run-val (.-value ~'_run-result) ~'_run-val (.-value ~'_run-result)
~'_run-err (.-error ~'_run-result) ~'_run-err (.-error ~'_run-result)
~'_end-inst (inst+nsecs ~'__inst ~'_run-nsecs) ~'_end-inst (inst+nsecs ~'__inst ~'_run-nsecs)
~'_msg_ ~'_msg_
(let [mf# ~msg-form] (let [mf# ~msg-form]
(if (fn? mf#) ; Undocumented, handy for `trace!`/`spy!`, etc. (if (fn? mf#) ; Undocumented, handy for `trace!`/`spy!`, etc.
(delay (mf# '~show-run-form ~show-run-val ~'_run-err ~'_run-nsecs)) (delay (mf# '~show-run-form ~show-run-val ~'_run-err ~'_run-nsecs))
mf#))] mf#))]
~record-form))] ~record-form))]
(if-not kvs-form (if-not kvs-form
record-form record-form
`(let [signal# ~record-form] `(let [signal# ~record-form]
(reduce-kv assoc signal# (.-kvs signal#)))))] (reduce-kv assoc signal# (.-kvs signal#)))))]
`(enc/bound-delay `(enc/bound-delay
;; Delay (cache) shared by all handlers, incl. `:let` eval, ;; Delay (cache) shared by all handlers, incl. `:let` eval,
;; signal construction, middleware, etc. Throws caught by handler. ;; signal construction, middleware, etc. Throws caught by handler.
~do-form ~do-form
(let [~@let-form ; Allow to throw, eval BEFORE data, msg, etc. (let [~@let-form ; Allow to throw, eval BEFORE data, msg, etc.
signal# ~signal-form] signal# ~signal-form]
;; Final unwrapped signal value visible to users/handler-fns, allow to throw ;; Final unwrapped signal value visible to users/handler-fns, allow to throw
(if-let [sig-middleware# ~middleware-form] (if-let [sig-middleware# ~middleware-form]
(sig-middleware# signal#) ; Apply signal middleware, can throw (sig-middleware# signal#) ; Apply signal middleware, can throw
(do signal#))))) (do signal#)))))
;; Trade-off: avoid double `run-form` expansion ;; Trade-off: avoid double `run-form` expansion
run-fn-form (when run-form `(fn [] ~run-form)) run-fn-form (when run-form `(fn [] ~run-form))
run-form* (when run-form `(~'__run-fn-form)) run-form* (when run-form `(~'__run-fn-form))
into-let-form into-let-form
(enc/cond! (enc/cond!
(not trace?) ; Don't trace (not trace?) ; Don't trace
`[~'__otel-context1 nil `[~'__otel-context1 nil
~'__uid ~(auto-> uid-form `(taoensso.telemere/*uid-fn* (if ~'__root0 false true))) ~'__uid ~(auto-> uid-form `(taoensso.telemere/*uid-fn* (if ~'__root0 false true)))
~'__root1 ~'__root0 ; Retain, but don't establish ~'__root1 ~'__root0 ; Retain, but don't establish
~'__run-result ~'__run-result
~(when run-form ~(when run-form
`(let [t0# (enc/now-nano*)] `(let [t0# (enc/now-nano*)]
(truss/try* (truss/try*
(do (RunResult. ~run-form* nil (- (enc/now-nano*) t0#))) (do (RunResult. ~run-form* nil (- (enc/now-nano*) t0#)))
(catch :all t# (RunResult. nil t# (- (enc/now-nano*) t0#))))))] (catch :all t# (RunResult. nil t# (- (enc/now-nano*) t0#))))))]
;; Trace without OpenTelemetry ;; Trace without OpenTelemetry
(or cljs? (not enabled:otel-tracing?)) (or cljs? (not enabled:otel-tracing?))
`[~'__otel-context1 nil `[~'__otel-context1 nil
~'__uid ~(auto-> uid-form `(taoensso.telemere/*uid-fn* (if ~'__root0 false true))) ~'__uid ~(auto-> uid-form `(taoensso.telemere/*uid-fn* (if ~'__root0 false true)))
~'__root1 (or ~'__root0 ~(when trace? `{:id ~'__id, :uid ~'__uid})) ~'__root1 (or ~'__root0 ~(when trace? `{:id ~'__id, :uid ~'__uid}))
~'__run-result ~'__run-result
~(when run-form ~(when run-form
`(binding [*trace-root* ~'__root1 `(binding [*trace-root* ~'__root1
*trace-parent* {:id ~'__id, :uid ~'__uid}] *trace-parent* {:id ~'__id, :uid ~'__uid}]
(let [t0# (enc/now-nano*)] (let [t0# (enc/now-nano*)]
(truss/try* (truss/try*
(do (RunResult. ~run-form* nil (- (enc/now-nano*) t0#))) (do (RunResult. ~run-form* nil (- (enc/now-nano*) t0#)))
(catch :all t# (RunResult. nil t# (- (enc/now-nano*) t0#)))))))] (catch :all t# (RunResult. nil t# (- (enc/now-nano*) t0#)))))))]
;; Trace with OpenTelemetry ;; Trace with OpenTelemetry
(and clj? enabled:otel-tracing?) (and clj? enabled:otel-tracing?)
`[~'__otel-context0 ~(get opts :otel/context `(otel-context)) ; Context `[~'__otel-context0 ~(get opts :otel/context `(otel-context)) ; Context
~'__otel-context1 ~(if run-form `(otel-context+span ~'__id ~'__inst ~'__otel-context0 ~(get opts :otel/span-kind)) ~'__otel-context0) ~'__otel-context1 ~(if run-form `(otel-context+span ~'__id ~'__inst ~'__otel-context0 ~(get opts :otel/span-kind)) ~'__otel-context0)
~'__uid ~(auto-> uid-form `(or (otel-span-id ~'__otel-context1) (com.taoensso.encore.Ids/genHexId16))) ~'__uid ~(auto-> uid-form `(or (otel-span-id ~'__otel-context1) (com.taoensso.encore.Ids/genHexId16)))
~'__root1 ~'__root1
(or ~'__root0 (or ~'__root0
~(when trace? ~(when trace?
`{:id ~'__id, :uid (or (otel-trace-id ~'__otel-context1) (com.taoensso.encore.Ids/genHexId32))})) `{:id ~'__id, :uid (or (otel-trace-id ~'__otel-context1) (com.taoensso.encore.Ids/genHexId32))}))
~'__run-result ~'__run-result
~(when run-form ~(when run-form
`(binding [*otel-context* ~'__otel-context1 `(binding [*otel-context* ~'__otel-context1
*trace-root* ~'__root1 *trace-root* ~'__root1
*trace-parent* {:id ~'__id, :uid ~'__uid}] *trace-parent* {:id ~'__id, :uid ~'__uid}]
(let [otel-scope# (.makeCurrent ~'__otel-context1) (let [otel-scope# (.makeCurrent ~'__otel-context1)
t0# (enc/now-nano*)] t0# (enc/now-nano*)]
(truss/try* (truss/try*
(do (RunResult. ~run-form* nil (- (enc/now-nano*) t0#))) (do (RunResult. ~run-form* nil (- (enc/now-nano*) t0#)))
(catch :all t# (RunResult. nil t# (- (enc/now-nano*) t0#))) (catch :all t# (RunResult. nil t# (- (enc/now-nano*) t0#)))
(finally (.close otel-scope#))))))]) (finally (.close otel-scope#))))))])
final-form final-form
;; Unless otherwise specified, allow errors to throw on call ;; Unless otherwise specified, allow errors to throw on call
`(let [~'__run-fn-form ~run-fn-form `(let [~'__run-fn-form ~run-fn-form
~'__kind ~kind-form ~'__kind ~kind-form
~'__ns ~ns-form ~'__ns ~ns-form
~'__id ~id-form ~'__id ~id-form
~'__level ~level-form] ~'__level ~level-form]
(enc/if-not ~allow? (enc/if-not ~allow?
~run-form* ~run-form*
(let [~'__inst ~inst-form (let [~'__inst ~inst-form
~'__thread ~thread-form ~'__thread ~thread-form
~'__root0 ~root-form0 ; ?{:keys [id uid]} ~'__root0 ~root-form0 ; ?{:keys [id uid]}
~@into-let-form ; Inject conditional bindings ~@into-let-form ; Inject conditional bindings
signal# ~signal-delay-form] signal# ~signal-delay-form]
(dispatch-signal! (dispatch-signal!
;; Unconditionally send same wrapped signal to all handlers. ;; Unconditionally send same wrapped signal to all handlers.
;; Each handler will use wrapper for handler filtering, ;; Each handler will use wrapper for handler filtering,
;; unwrapping (realizing) only allowed signals. ;; unwrapping (realizing) only allowed signals.
(WrappedSignal. ~'__kind ~'__ns ~'__id ~'__level signal#)) (WrappedSignal. ~'__kind ~'__ns ~'__id ~'__level signal#))
(if ~'__run-result (if ~'__run-result
( ~'__run-result signal#) ( ~'__run-result signal#)
true))))] true))))]
(if-let [iife-wrap? true #_cljs?] (if-let [iife-wrap? true #_cljs?]
;; Small perf hit to improve compatibility within `go` and other IOC-style bodies ;; Small perf hit to improve compatibility within `go` and other IOC-style bodies
`((fn [] ~final-form)) `((fn [] ~final-form))
(do final-form))))))) (do final-form))))))))
(comment (comment
(with-signal (signal! {:level :warn :let [x :x] :msg ["Test" "message" x] :data {:a :A :x x} :run (+ 1 2)})) (with-signal (signal! {:level :warn :let [x :x] :msg ["Test" "message" x] :data {:a :A :x x} :run (+ 1 2)}))