mirror of
https://github.com/taoensso/telemere.git
synced 2025-12-16 17:41:12 +00:00
[mod] Rename "middleware" -> "transform" (xfn)
Users caught by this change should receive a clear compile-time error.
Apologies for the nuissance!! This change is part of a final review
of names before the release of v1 final.
Motivations:
- "xfn" is a lot shorter than "middleware", making it more
convenient to use at signal calls, compare:
(log! {:middleware my-fn} "msg")
(log! {:xfn my-fn} "msg"}
- "middleware" was originally chosen to match Timbre's terminology,
but actually carries some misleading connotations that in hindsight
are probably better avoided while we still have the chance to change
this.
This commit is contained in:
parent
c78eb07385
commit
7cccf672f5
14 changed files with 113 additions and 114 deletions
13
README.md
13
README.md
|
|
@ -113,15 +113,14 @@ It enables you to write code that is **information-verbose by default**.
|
|||
;; Set minimum level for `event!` signals for particular ns pattern
|
||||
(t/set-min-level! :event "taoensso.sente.*" :warn)
|
||||
|
||||
;; Use middleware to:
|
||||
;; - Transform signals
|
||||
;; - Filter signals by arb conditions (incl. data/content)
|
||||
;; Use transforms (xfns) to filter and/or arbitrarily modify signals
|
||||
;; by signal data/content/etc.
|
||||
|
||||
(t/set-middleware!
|
||||
(t/set-xfn!
|
||||
(fn [signal]
|
||||
(if (-> signal :data :skip-me?)
|
||||
nil ; Filter signal (don't handle)
|
||||
(assoc signal :passed-through-middleware? true))))
|
||||
(assoc signal :transformed? true))))
|
||||
|
||||
(t/with-signal (t/event! ::my-id {:data {:skip-me? true}})) ; => nil
|
||||
(t/with-signal (t/event! ::my-id {:data {:skip-me? false}})) ; => {...}
|
||||
|
|
@ -251,7 +250,7 @@ Detailed help is available without leaving your IDE:
|
|||
| :---------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------- |
|
||||
| [`help:signal-creators`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-creators) | Creating signals |
|
||||
| [`help:signal-options`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-options) | Options when creating signals |
|
||||
| [`help:signal-content`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content) | Signal content (map given to middleware/handlers) |
|
||||
| [`help:signal-content`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content) | Signal content (map given to transforms/handlers) |
|
||||
| [`help:filters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:filters) | Signal filtering and transformation |
|
||||
| [`help:handlers`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:handlers) | Signal handler management |
|
||||
| [`help:handler-dispatch-options`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:handler-dispatch-options) | Signal handler dispatch options |
|
||||
|
|
@ -314,7 +313,7 @@ You can also easily [write your own handlers](../../wiki/4-Handlers#writing-hand
|
|||
|
||||
## Community
|
||||
|
||||
My plan for Telemere is to offer a **stable core of limited scope**, then to focus on making it as easy for the **community** to write additional stuff like handlers, middleware, and utils.
|
||||
My plan for Telemere is to offer a **stable core of limited scope**, then to focus on making it as easy for the **community** to write additional stuff like handlers, transforms, and utils.
|
||||
|
||||
See [here](../../wiki/8-Community) for community resources.
|
||||
|
||||
|
|
|
|||
|
|
@ -59,15 +59,14 @@
|
|||
;; Set minimum level for `event!` signals for particular ns pattern
|
||||
(t/set-min-level! :event "taoensso.sente.*" :warn)
|
||||
|
||||
;; Use middleware to:
|
||||
;; - Transform signals
|
||||
;; - Filter signals by arb conditions (incl. data/content)
|
||||
;; Use transforms (xfns) to filter and/or arbitrarily modify signals
|
||||
;; by signal data/content/etc.
|
||||
|
||||
(t/set-middleware!
|
||||
(t/set-xfn!
|
||||
(fn [signal]
|
||||
(if (-> signal :data :skip-me?)
|
||||
nil ; Filter signal (don't handle)
|
||||
(assoc signal :passed-through-middleware? true))))
|
||||
(assoc signal :transformed? true))))
|
||||
|
||||
(t/with-signal (t/event! ::my-id {:data {:skip-me? true}})) ; => nil
|
||||
(t/with-signal (t/event! ::my-id {:data {:skip-me? false}})) ; => {...}
|
||||
|
|
@ -333,16 +332,16 @@
|
|||
|
||||
(t/with-signal
|
||||
(t/event! ::my-id
|
||||
{:my-middleware-data "foo"
|
||||
:my-handler-data "bar"}))
|
||||
{:my-data-for-xfn "foo"
|
||||
:my-data-for-handler "bar"}))
|
||||
|
||||
;; %>
|
||||
;; {;; App-level kvs included inline (assoc'd to signal root)
|
||||
;; :my-middleware-data "foo"
|
||||
;; :my-handler-data "bar"
|
||||
;; :my-data-for-xfn "foo"
|
||||
;; :my-data-for-handler "bar"
|
||||
;; :kvs ; And also collected together under ":kvs" key
|
||||
;; {:my-middleware-data "foo"
|
||||
;; :my-handler-data "bar"}
|
||||
;; {:my-data-for-xfn "foo"
|
||||
;; :my-data-for-handler "bar"}
|
||||
;; ... }
|
||||
|
||||
;;;; Misc extra examples
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
Signals are maps with {:keys [inst id ns level data msg_ ...]}, though they
|
||||
can be modified by signal and/or handler middleware.
|
||||
can be modified by call and/or handler transform (xfns).
|
||||
|
||||
Default signal keys:
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ Default signal keys:
|
|||
|
||||
<kvs> ---------- Other arb app-level ?kvs given to signal creator. Typically NOT included
|
||||
in handler output, so a great way to provide custom data/opts for use
|
||||
(only) by custom middleware/handlers.
|
||||
(only) by custom transforms/handlers.
|
||||
|
||||
If anything is unclear, please ping me (@ptaoussanis) so that I can improve these docs!
|
||||
|
||||
|
|
|
|||
|
|
@ -25,17 +25,18 @@ All options are available for all signal creator calls:
|
|||
`:coords` ------ Custom ?[line column] to override auto signal callsite info
|
||||
|
||||
`:elidable?` --- Should signal be subject to compile-time elision? (Default: true)
|
||||
`:trace?` ------ Should tracing be enabled for `:run` form?
|
||||
|
||||
`:sample-rate` - ?rate ∈ℝ[0,1] for signal 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 `taoensso.telemere/rate-limiter`, see its docstring for details
|
||||
`:rate-limit-by` When present, rate limits will be enforced independently for each id (any Clojure value!)
|
||||
`:middleware` -- Optional (fn [signal]) => ?modified-signal to apply when signal is created, as per `with-middleware`
|
||||
`:middleware+` - Optional (fn [signal]) => ?modified-signal to apply when signal is created, as per `with-middleware+`
|
||||
`:trace?` ------ Should tracing be enabled for `:run` form?
|
||||
`:xfn` --------- Optional transform (fn [signal]) => ?modified-signal to apply when signal is created, as per `with-xfn`
|
||||
`:xfn+` -------- Optional extra transform (fn [signal]) => ?modified-signal to apply when signal is created, as per `with-xfn+`
|
||||
|
||||
<kvs> ---------- Other arb app-level ?kvs to incl. in signal. Typically NOT included in
|
||||
handler output, so a great way to provide custom data/opts for use
|
||||
(only) by custom middleware/handlers.
|
||||
(only) by custom transforms/handlers.
|
||||
|
||||
If anything is unclear, please ping me (@ptaoussanis) so that I can improve these docs!
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
;; Via `sigs/def-api`
|
||||
without-filters with-kind-filter with-ns-filter with-id-filter
|
||||
with-min-level with-handler with-handler+
|
||||
with-ctx with-ctx+ with-middleware with-middleware+]])))
|
||||
with-ctx with-ctx+ with-xfn with-xfn+]])))
|
||||
|
||||
(comment
|
||||
(remove-ns (symbol (str *ns*)))
|
||||
|
|
@ -50,7 +50,7 @@
|
|||
add-handler! remove-handler! stop-handlers!
|
||||
|
||||
^:dynamic *ctx* set-ctx! #?(:clj with-ctx) #?(:clj with-ctx+)
|
||||
^:dynamic *middleware* set-middleware! #?(:clj with-middleware) #?(:clj with-middleware+))
|
||||
^:dynamic *xfn* set-xfn! #?(:clj with-xfn) #?(:clj with-xfn+))
|
||||
|
||||
(sigs/def-api
|
||||
{:sf-arity 4
|
||||
|
|
@ -73,7 +73,7 @@
|
|||
enc/chance
|
||||
enc/rate-limiter
|
||||
enc/newline
|
||||
enc/comp-middleware
|
||||
sigs/comp-xfn
|
||||
sigs/default-handler-dispatch-opts
|
||||
#?(:clj truss/keep-callsite)
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
#?(:clj
|
||||
(enc/declare-remote
|
||||
^:dynamic taoensso.telemere/*ctx*
|
||||
^:dynamic taoensso.telemere/*middleware*
|
||||
^:dynamic taoensso.telemere/*xfn*
|
||||
^:dynamic taoensso.telemere/*uid-fn*
|
||||
^:dynamic taoensso.telemere/*otel-tracer*))
|
||||
|
||||
|
|
@ -393,7 +393,7 @@
|
|||
'( [& opts-kvs]
|
||||
[{:as opts-map :keys
|
||||
[#_elide? #_allow? #_callsite-id, ; Undocumented
|
||||
elidable? coords #_inst #_uid #_middleware #_middleware+,
|
||||
elidable? coords #_inst #_uid #_xfn #_xfn+,
|
||||
sample-rate kind ns id level when rate-limit rate-limit-by,
|
||||
#_ctx #_ctx+ #_parent #_root #_trace?, #_do #_let #_data #_msg #_error #_run #_& #_kvs]}])
|
||||
|
||||
|
|
@ -401,7 +401,7 @@
|
|||
'( [& opts-kvs]
|
||||
[{:as opts-map :keys
|
||||
[#_elide? #_allow? #_callsite-id, ; Undocumented
|
||||
elidable? coords inst uid middleware middleware+,
|
||||
elidable? coords inst uid xfn xfn+,
|
||||
sample-rate kind ns id level when rate-limit rate-limit-by,
|
||||
ctx ctx+ parent root trace?, do let data msg error run & kvs]}])
|
||||
|
||||
|
|
@ -410,7 +410,7 @@
|
|||
[level msg]
|
||||
[{:as opts-map :keys
|
||||
[#_elide? #_allow? #_callsite-id,
|
||||
elidable? coords inst uid middleware middleware+,
|
||||
elidable? coords inst uid xfn xfn+,
|
||||
sample-rate kind ns id level when rate-limit rate-limit-by,
|
||||
ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}
|
||||
msg])
|
||||
|
|
@ -421,7 +421,7 @@
|
|||
[id
|
||||
{:as opts-map :keys
|
||||
[#_elide? #_allow? #_callsite-id,
|
||||
elidable? coords inst uid middleware middleware+,
|
||||
elidable? coords inst uid xfn xfn+,
|
||||
sample-rate kind ns id level when rate-limit rate-limit-by,
|
||||
ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}])
|
||||
|
||||
|
|
@ -430,7 +430,7 @@
|
|||
[id run]
|
||||
[{:as opts-map :keys
|
||||
[#_elide? #_allow? #_callsite-id,
|
||||
elidable? coords inst uid middleware middleware+,
|
||||
elidable? coords inst uid xfn xfn+,
|
||||
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])
|
||||
|
|
@ -440,7 +440,7 @@
|
|||
[level run]
|
||||
[{:as opts-map :keys
|
||||
[#_elide? #_allow? #_callsite-id,
|
||||
elidable? coords inst uid middleware middleware+,
|
||||
elidable? coords inst uid xfn xfn+,
|
||||
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])
|
||||
|
|
@ -450,7 +450,7 @@
|
|||
[id error]
|
||||
[{:as opts-map :keys
|
||||
[#_elide? #_allow? #_callsite-id,
|
||||
elidable? coords inst uid middleware middleware+,
|
||||
elidable? coords inst uid xfn xfn+,
|
||||
sample-rate kind ns id level when rate-limit rate-limit-by,
|
||||
ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}
|
||||
error])
|
||||
|
|
@ -460,7 +460,7 @@
|
|||
[id run]
|
||||
[{:as opts-map :keys
|
||||
[#_elide? #_allow? #_callsite-id, catch-val,
|
||||
elidable? coords inst uid middleware middleware+,
|
||||
elidable? coords inst uid xfn xfn+,
|
||||
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])
|
||||
|
|
@ -470,7 +470,7 @@
|
|||
[opts-or-id]
|
||||
[{:as opts-map :keys
|
||||
[#_elide? #_allow? #_callsite-id,
|
||||
elidable? coords inst uid middleware middleware+,
|
||||
elidable? coords inst uid xfn xfn+,
|
||||
sample-rate kind ns id level when rate-limit rate-limit-by,
|
||||
ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}])
|
||||
|
||||
|
|
@ -618,15 +618,15 @@
|
|||
`(taoensso.encore.signals/update-ctx taoensso.telemere/*ctx* ~ctx+)
|
||||
(get opts :ctx `taoensso.telemere/*ctx*))
|
||||
|
||||
middleware-form
|
||||
(if-let [middleware+ (get opts :middleware+)]
|
||||
`(taoensso.encore/comp-middleware taoensso.telemere/*middleware* ~middleware+)
|
||||
(get opts :middleware `taoensso.telemere/*middleware*))
|
||||
xfn-form
|
||||
(if-let [xfn+ (get opts :xfn+)]
|
||||
`(taoensso.encore.signals/comp-xfn taoensso.telemere/*xfn* ~xfn+)
|
||||
(get opts :xfn `taoensso.telemere/*xfn*))
|
||||
|
||||
kvs-form
|
||||
(not-empty
|
||||
(dissoc opts
|
||||
:elidable? :coords :inst :uid :middleware :middleware+,
|
||||
:elidable? :coords :inst :uid :xfn :xfn+,
|
||||
:sample-rate :ns :kind :id :level :filter :when #_:rate-limit #_:rate-limit-by,
|
||||
:ctx :ctx+ :parent #_:trace?, :do :let :data :msg :error,
|
||||
:run :run-form :run-val, :elide? :allow? #_:callsite-id :otel/context))
|
||||
|
|
@ -677,15 +677,15 @@
|
|||
|
||||
`(enc/bound-delay
|
||||
;; Delay (cache) shared by all handlers, incl. `:let` eval,
|
||||
;; signal construction, middleware, etc. Throws caught by handler.
|
||||
;; signal construction, transform (xfn), etc. Throws caught by handler.
|
||||
~do-form
|
||||
(let [~@let-form ; Allow to throw, eval BEFORE data, msg, etc.
|
||||
signal# ~signal-form]
|
||||
|
||||
;; Final unwrapped signal value visible to users/handler-fns, allow to throw
|
||||
(if-let [sig-middleware# ~middleware-form]
|
||||
(sig-middleware# signal#) ; Apply signal middleware, can throw
|
||||
(do signal#)))))
|
||||
(if-let [xfn# ~xfn-form]
|
||||
(xfn# signal#) ; Apply call transform, can throw
|
||||
(do signal#)))))
|
||||
|
||||
;; Trade-off: avoid double `run-form` expansion
|
||||
run-fn-form (when run-form `(fn [] ~run-form))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
(ns taoensso.telemere.utils
|
||||
"Misc utils useful for Telemere handlers, middleware, etc."
|
||||
"Misc utils useful for Telemere handlers, transforms, etc."
|
||||
(:refer-clojure :exclude [newline])
|
||||
(:require
|
||||
[clojure.string :as str]
|
||||
|
|
|
|||
|
|
@ -250,35 +250,35 @@
|
|||
(with-sig (sig! {:level :info, :ctx+ {:baz :qux}})))]
|
||||
(is (sm? sv {:ctx {:foo :bar, :baz :qux}}) "`*ctx*` can be updated via call opt"))])
|
||||
|
||||
(testing "Middleware"
|
||||
[(testing "Dynamic middleware (`*middleware*`)"
|
||||
[(is (sm? (tel/with-middleware nil (with-sig (sig! {:level :info }))) {:level :info }) "nil middleware ~ identity")
|
||||
(is (sm? (tel/with-middleware identity (with-sig (sig! {:level :info }))) {:level :info }) "nil middleware ~ identity")
|
||||
(is (sm? (tel/with-middleware #(assoc % :foo 1) (with-sig (sig! {:level :info }))) {:level :info, :foo 1 }))
|
||||
(is (sm? (tel/with-middleware #(assoc % :foo 1) (with-sig (sig! {:level :info, :middleware #(assoc % :foo 2)}))) {:level :info, :foo 2 }) "call > dynamic")
|
||||
(is (sm? (tel/with-middleware #(assoc % :foo 1) (with-sig (sig! {:level :info, :middleware nil}))) {:level :info, :foo :submap/nx}) "call > dynamic")
|
||||
(is (= (tel/with-middleware #(do nil) (with-sig (sig! {:level :info }))) nil) "return nil => suppress")
|
||||
(is (sm? (tel/with-middleware #(do nil) (with-sig (sig! {:level :info, :middleware nil}))) {:level :info}) "call > dynamic")])
|
||||
(testing "Transforms"
|
||||
[(testing "Dynamic transforms (`*xfn*`)"
|
||||
[(is (sm? (tel/with-xfn nil (with-sig (sig! {:level :info }))) {:level :info }) "nil xfn ~ identity")
|
||||
(is (sm? (tel/with-xfn identity (with-sig (sig! {:level :info }))) {:level :info }) "nil xfn ~ identity")
|
||||
(is (sm? (tel/with-xfn #(assoc % :foo 1) (with-sig (sig! {:level :info }))) {:level :info, :foo 1 }))
|
||||
(is (sm? (tel/with-xfn #(assoc % :foo 1) (with-sig (sig! {:level :info, :xfn #(assoc % :foo 2)}))) {:level :info, :foo 2 }) "call > dynamic")
|
||||
(is (sm? (tel/with-xfn #(assoc % :foo 1) (with-sig (sig! {:level :info, :xfn nil}))) {:level :info, :foo :submap/nx}) "call > dynamic")
|
||||
(is (= (tel/with-xfn #(do nil) (with-sig (sig! {:level :info }))) nil) "return nil => suppress")
|
||||
(is (sm? (tel/with-xfn #(do nil) (with-sig (sig! {:level :info, :xfn nil}))) {:level :info}) "call > dynamic")])
|
||||
|
||||
(testing "Call middleware"
|
||||
(testing "Call transforms"
|
||||
(let [c (enc/counter)
|
||||
{rv1 :value, [sv1] :signals} (with-sigs :raw nil (sig! {:level :info, :run (c), :middleware (tel/comp-middleware #(assoc % :m1 (c)) #(assoc % :m2 (c)))}))
|
||||
{rv2 :value, [sv2] :signals} (with-sigs :raw nil (sig! {:level :info, :run (c), :middleware (tel/comp-middleware #(assoc % :m1 (c)) #(assoc % :m2 (c))), :allow? false}))
|
||||
{rv3 :value, [sv3] :signals} (with-sigs :raw nil (sig! {:level :info, :run (c), :middleware (tel/comp-middleware #(assoc % :m1 (c)) #(assoc % :m2 (c)))}))
|
||||
{rv4 :value, [sv4] :signals} (with-sigs :raw nil (sig! {:level :info, :middleware (fn [_] "signal-value")}))
|
||||
{rv5 :value, [sv5] :signals} (with-sigs :raw nil (sig! {:level :info, :middleware (fn [_] nil)}))]
|
||||
{rv1 :value, [sv1] :signals} (with-sigs :raw nil (sig! {:level :info, :run (c), :xfn (tel/comp-xfn #(assoc % :m1 (c)) #(assoc % :m2 (c)))}))
|
||||
{rv2 :value, [sv2] :signals} (with-sigs :raw nil (sig! {:level :info, :run (c), :xfn (tel/comp-xfn #(assoc % :m1 (c)) #(assoc % :m2 (c))), :allow? false}))
|
||||
{rv3 :value, [sv3] :signals} (with-sigs :raw nil (sig! {:level :info, :run (c), :xfn (tel/comp-xfn #(assoc % :m1 (c)) #(assoc % :m2 (c)))}))
|
||||
{rv4 :value, [sv4] :signals} (with-sigs :raw nil (sig! {:level :info, :xfn (fn [_] "signal-value")}))
|
||||
{rv5 :value, [sv5] :signals} (with-sigs :raw nil (sig! {:level :info, :xfn (fn [_] nil)}))]
|
||||
|
||||
[(is (= rv1 0)) (is (sm? sv1 {:m1 1 :m2 2}))
|
||||
(is (= rv2 3)) (is (nil? sv2))
|
||||
(is (= rv3 4)) (is (sm? sv3 {:m1 5 :m2 6}))
|
||||
(is (= rv4 true)) (is (= sv4 "signal-value"))
|
||||
(is (= rv5 true)) (is (nil? sv5))
|
||||
(is (= @c 7) "3x run + 4x middleware")]))
|
||||
(is (= @c 7) "3x run + 4x xfn")]))
|
||||
|
||||
(testing "Mixed middleware"
|
||||
(testing "Mixed transforms"
|
||||
[(let [sv
|
||||
(binding [tel/*middleware* #(assoc % :foo true)]
|
||||
(with-sig (sig! {:level :info, :middleware+ #(assoc % :bar true)})))]
|
||||
(binding [tel/*xfn* #(assoc % :foo true)]
|
||||
(with-sig (sig! {:level :info, :xfn+ #(assoc % :bar true)})))]
|
||||
(is (sm? sv {:foo true, :bar true})))])])
|
||||
|
||||
#?(:clj
|
||||
|
|
@ -293,35 +293,35 @@
|
|||
|
||||
(deftest _handlers
|
||||
;; Basic handler tests are in Encore
|
||||
[(testing "Handler middleware"
|
||||
[(testing "Handler transforms"
|
||||
(let [c (enc/counter)
|
||||
sv-h1_ (atom nil)
|
||||
sv-h2_ (atom nil)
|
||||
wh1 (sigs/wrap-handler :hid1 (fn [sv] (reset! sv-h1_ sv)) nil {:async nil, :middleware (tel/comp-middleware #(assoc % :hm1 (c)) #(assoc % :hm2 (c)))})
|
||||
wh2 (sigs/wrap-handler :hid2 (fn [sv] (reset! sv-h2_ sv)) nil {:async nil, :middleware (tel/comp-middleware #(assoc % :hm1 (c)) #(assoc % :hm2 (c)))})]
|
||||
wh1 (sigs/wrap-handler :hid1 (fn [sv] (reset! sv-h1_ sv)) nil {:async nil, :xfn (tel/comp-xfn #(assoc % :hm1 (c)) #(assoc % :hm2 (c)))})
|
||||
wh2 (sigs/wrap-handler :hid2 (fn [sv] (reset! sv-h2_ sv)) nil {:async nil, :xfn (tel/comp-xfn #(assoc % :hm1 (c)) #(assoc % :hm2 (c)))})]
|
||||
|
||||
;; Note that call middleware output is cached and shared across all handlers
|
||||
;; Note that call xfn output is cached and shared across all handlers
|
||||
(binding [impl/*sig-handlers* [wh1 wh2]]
|
||||
(let [;; 1x run + 4x handler middleware + 2x call middleware = 7x
|
||||
rv1 (sig! {:level :info, :run (c), :middleware (tel/comp-middleware #(assoc % :m1 (c)) #(assoc % :m2 (c)))})
|
||||
(let [;; 1x run + 4x handler xfn + 2x call xfn = 7x
|
||||
rv1 (sig! {:level :info, :run (c), :xfn (tel/comp-xfn #(assoc % :m1 (c)) #(assoc % :m2 (c)))})
|
||||
sv1-h1 @sv-h1_
|
||||
sv1-h2 @sv-h2_
|
||||
c1 @c
|
||||
|
||||
;; 1x run
|
||||
rv2 (sig! {:level :info, :run (c), :middleware (tel/comp-middleware #(assoc % :m1 (c)) #(assoc % :m2 (c))), :allow? false})
|
||||
rv2 (sig! {:level :info, :run (c), :xfn (tel/comp-xfn #(assoc % :m1 (c)) #(assoc % :m2 (c))), :allow? false})
|
||||
sv2-h1 @sv-h1_
|
||||
sv2-h2 @sv-h2_
|
||||
c2 @c ; 8
|
||||
|
||||
;; 1x run + 4x handler middleware + 2x call middleware = 7x
|
||||
rv3 (sig! {:level :info, :run (c), :middleware (tel/comp-middleware #(assoc % :m1 (c)) #(assoc % :m2 (c)))})
|
||||
;; 1x run + 4x handler xfn + 2x call xfn = 7x
|
||||
rv3 (sig! {:level :info, :run (c), :xfn (tel/comp-xfn #(assoc % :m1 (c)) #(assoc % :m2 (c)))})
|
||||
sv3-h1 @sv-h1_
|
||||
sv3-h2 @sv-h2_
|
||||
c3 @c ; 15
|
||||
|
||||
;; 4x handler middleware
|
||||
rv4 (sig! {:level :info, :middleware (fn [_] {:my-sig-val? true})})
|
||||
;; 4x handler xfn
|
||||
rv4 (sig! {:level :info, :xfn (fn [_] {:my-sig-val? true})})
|
||||
sv4-h1 @sv-h1_
|
||||
sv4-h2 @sv-h2_
|
||||
c4 @c]
|
||||
|
|
@ -330,10 +330,10 @@
|
|||
(is (= rv2 7)) (is (sm? sv2-h1 {:m1 1, :m2 2, :hm1 3, :hm2 4})) (is (sm? sv2-h2 {:m1 1, :m2 2, :hm1 5, :hm2 6}))
|
||||
(is (= rv3 8)) (is (sm? sv3-h1 {:m1 9, :m2 10, :hm1 11, :hm2 12})) (is (sm? sv3-h2 {:m1 9, :m2 10, :hm1 13, :hm2 14}))
|
||||
(is (= rv4 true)) (is (sm? sv4-h1 {:my-sig-val? true, :hm1 15, :hm2 16})) (is (sm? sv4-h2 {:my-sig-val? true, :hm1 17, :hm2 18}))
|
||||
(is (= c1 7) "1x run + 4x handler middleware + 2x call middleware")
|
||||
(is (= c2 8) "2x run + 4x handler middleware + 2x call middleware")
|
||||
(is (= c3 15) "3x run + 8x handler middleware + 4x call middleware")
|
||||
(is (= c4 19) "3x run + 12x handler middleware + 4x call middleware")]))))
|
||||
(is (= c1 7) "1x run + 4x handler xfn + 2x call xfn")
|
||||
(is (= c2 8) "2x run + 4x handler xfn + 2x call xfn")
|
||||
(is (= c3 15) "3x run + 8x handler xfn + 4x call xfn")
|
||||
(is (= c4 19) "3x run + 12x handler xfn + 4x call xfn")]))))
|
||||
|
||||
(testing "Handler binding conveyance"
|
||||
(let [a (atom nil)
|
||||
|
|
@ -379,7 +379,7 @@
|
|||
(test1 64 {:async {:mode :dropping, :buffer-size 64}})
|
||||
(test1 64 {:async {:mode :sliding, :buffer-size 64}})]))))))])
|
||||
|
||||
(def ^:dynamic *throwing-handler-middleware?* false)
|
||||
(def ^:dynamic *throwing-handler-xfn?* false)
|
||||
|
||||
(deftest _throwing
|
||||
(let [sv_ (atom :nx)
|
||||
|
|
@ -393,7 +393,7 @@
|
|||
(tel/with-handler :hid1
|
||||
(fn [sv] (force (:data sv)) (reset! sv_ sv))
|
||||
{:async nil, :error-fn (fn [x] (reset! error_ x)), :rl-error nil,
|
||||
:middleware (fn [sv] (if *throwing-handler-middleware?* (ex1!) sv))}
|
||||
:xfn (fn [sv] (if *throwing-handler-xfn?* (ex1!) sv))}
|
||||
|
||||
[(is (->> (sig! {:level :info, :when (ex1!)}) (throws? :ex-info "Ex1")) "`~filterable-expansion/allow` throws at call")
|
||||
(is (->> (sig! {:level :info, :inst (ex1!)}) (throws? :ex-info "Ex1")) "`~inst-form` throws at call")
|
||||
|
|
@ -409,15 +409,15 @@
|
|||
(is (= @sv_ :nx))
|
||||
(is (sm? @error_ {:handler-id :hid1, :error ex1}))])
|
||||
|
||||
(testing "Throwing call middleware"
|
||||
(testing "Throwing call transform"
|
||||
(reset-state!)
|
||||
[(is (true? (sig! {:level :info, :middleware (fn [_] (ex1!))})))
|
||||
[(is (true? (sig! {:level :info, :xfn (fn [_] (ex1!))})))
|
||||
(is (= @sv_ :nx))
|
||||
(is (sm? @error_ {:handler-id :hid1, :error ex1}))])
|
||||
|
||||
(testing "Throwing handler middleware"
|
||||
(testing "Throwing handler transform"
|
||||
(reset-state!)
|
||||
(binding [*throwing-handler-middleware?* true]
|
||||
(binding [*throwing-handler-xfn?* true]
|
||||
[(is (true? (sig! {:level :info})))
|
||||
(is (= @sv_ :nx))
|
||||
(is (sm? @error_ {:handler-id :hid1, :error ex1}))]))
|
||||
|
|
|
|||
|
|
@ -175,10 +175,10 @@ A signal will be provided to a handler iff ALL of the following are true:
|
|||
- a. Compile time: not applicable
|
||||
- b. Runtime: sample rate, kind, ns, id, level, when fn, rate limit
|
||||
|
||||
- 3. **Call middleware** `(fn [signal]) => ?modified-signal` returns non-nil
|
||||
- 4. **Handler middleware** `(fn [signal]) => ?modified-signal` returns non-nil
|
||||
- 3. **Call transform** `(fn [signal]) => ?modified-signal` returns non-nil
|
||||
- 4. **Handler transform** `(fn [signal]) => ?modified-signal` returns non-nil
|
||||
|
||||
> Middleware provides a flexible way to modify and/or filter signals by arbitrary signal data/content conditions (return nil to skip).
|
||||
> Transform fns provides a flexible way to modify and/or filter signals by arbitrary signal data/content conditions (return nil to skip handling).
|
||||
|
||||
Quick examples of some basic filtering:
|
||||
|
||||
|
|
@ -206,7 +206,7 @@ Telemere includes extensive internal help docstrings:
|
|||
| :---------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------- |
|
||||
| [`help:signal-creators`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-creators) | Creating signals |
|
||||
| [`help:signal-options`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-options) | Options when creating signals |
|
||||
| [`help:signal-content`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content) | Signal content (map given to middleware/handlers) |
|
||||
| [`help:signal-content`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content) | Signal content (map given to transforms/handlers) |
|
||||
| [`help:filters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:filters) | Signal filtering and transformation |
|
||||
| [`help:handlers`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:handlers) | Signal handler management |
|
||||
| [`help:handler-dispatch-options`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:handler-dispatch-options) | Signal handler dispatch options |
|
||||
|
|
|
|||
|
|
@ -16,4 +16,4 @@ This flow is visualized below:
|
|||
<img src="https://raw.githubusercontent.com/taoensso/telemere/master/imgs/signal-flow.svg" alt="Telemere signal flowchart" width="640"/>
|
||||
|
||||
- `A/sync queue` semantics are specified via [handler dispatch options](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:handler-dispatch-options).
|
||||
- The shared **call middleware cache** is super useful when doing signal transformations that are expensive and/or involve side effects (like syncing with another service/db to get a unique tx id, etc.).
|
||||
- The shared **call transform** cache is super useful when doing signal transformations that are expensive and/or involve side effects (like syncing with another service/db to get a unique tx id, etc.).
|
||||
|
|
@ -12,10 +12,10 @@ A signal will be provided to a handler iff ALL of the following are true:
|
|||
- a. Compile time: not applicable
|
||||
- b. Runtime: sample rate, kind, ns, id, level, when fn, rate limit
|
||||
|
||||
- 3. **Call middleware** `(fn [signal]) => ?modified-signal` returns non-nil
|
||||
- 4. **Handler middleware** `(fn [signal]) => ?modified-signal` returns non-nil
|
||||
- 3. **Call transform** `(fn [signal]) => ?modified-signal` returns non-nil
|
||||
- 4. **Handler transform** `(fn [signal]) => ?modified-signal` returns non-nil
|
||||
|
||||
> Middleware provides a flexible way to modify and/or filter signals by arbitrary signal data/content conditions (return nil to skip).
|
||||
> Transform fns provides a flexible way to modify and/or filter signals by arbitrary signal data/content conditions (return nil to skip handling).
|
||||
|
||||
See [`help:filters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:filters) for more about filtering.
|
||||
|
||||
|
|
|
|||
|
|
@ -48,11 +48,11 @@ There's two kinds of config relevant to all signal handlers:
|
|||
|
||||
## Dispatch opts
|
||||
|
||||
Handler dispatch opts includes dispatch priority (determines order in which handlers are called), handler filtering, handler middleware, a/sync queue semantics, back-pressure opts, etc.
|
||||
Handler dispatch opts includes dispatch priority (determines order in which handlers are called), handler filtering, handler transform, a/sync queue semantics, back-pressure opts, etc.
|
||||
|
||||
See [`help:handler-dispatch-options`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:handler-dispatch-options) for full info, and [`default-handler-dispatch-opts`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#default-handler-dispatch-opts) for defaults.
|
||||
|
||||
Note that handler middleware in particular is an easily overlooked but powerful feature, allowing you to arbitrarily transform and/or filter every [signal map](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content) before it is given to each handler.
|
||||
Note that the handler transform is an easily overlooked but powerful feature, allowing you to arbitrarily modify and/or filter every [signal map](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content) before it is given to each handler.
|
||||
|
||||
## Handler-specific opts
|
||||
|
||||
|
|
@ -120,27 +120,27 @@ Note that when writing JSON with Clojure, you *must* provide an appropriate `pr-
|
|||
|
||||
### Handler-specific per-signal kvs
|
||||
|
||||
Telemere includes a handy mechanism for including arbitrary app-level data/opts in individual signals for use by custom middleware and/or handlers.
|
||||
Telemere includes a handy mechanism for including arbitrary app-level data/opts in individual signals for use by custom transforms and/or handlers.
|
||||
|
||||
Any *non-standard* (app-level) keys you include in your signal constructor opts will automatically be included in created signals, e.g.:
|
||||
|
||||
```clojure
|
||||
(t/with-signal
|
||||
(t/event! ::my-id
|
||||
{:my-middleware-data "foo"
|
||||
:my-handler-data "bar"}))
|
||||
{:my-data-for-xfn "foo"
|
||||
:my-data-for-handler "bar"}))
|
||||
|
||||
;; %>
|
||||
;; {;; App-level kvs included inline (assoc'd to signal root)
|
||||
;; :my-middleware-data "foo"
|
||||
;; :my-handler-data "bar"
|
||||
;; :my-data-for-xfn "foo"
|
||||
;; :my-data-for-handler "bar"
|
||||
;; :kvs ; And also collected together under ":kvs" key
|
||||
;; {:my-middleware-data "foo"
|
||||
;; :my-handler-data "bar"}
|
||||
;; {:my-data-for-xfn "foo"
|
||||
;; :my-data-for-handler "bar"}
|
||||
;; ... }
|
||||
```
|
||||
|
||||
These app-level data/opts are typically NOT included by default in handler output, making them a great way to convey data/opts to custom middleware/handlers.
|
||||
These app-level data/opts are typically NOT included by default in handler output, making them a great way to convey data/opts to custom transforms/handlers.
|
||||
|
||||
# Managing handlers
|
||||
|
||||
|
|
|
|||
|
|
@ -80,9 +80,9 @@ Consider the [differences](https://www.youtube.com/watch?v=oyLBGkS5ICk) between
|
|||
|
||||
This way you can see all your ids in one place, and precise info on when ids were added/removed/changed.
|
||||
|
||||
- Use **signal call middleware** to your advantage.
|
||||
- Use **signal call transforms** to your advantage.
|
||||
|
||||
The result of call middleware is cached and *shared between all handlers* making it an efficient place to transform signals. For this reason - prefer signal middleware to handler middleware when possible/convenient.
|
||||
The result of call-side signal transforms is cached and *shared between all handlers* making it an efficient place to modify signals going to >1 handler.
|
||||
|
||||
- Signal and handler **sampling is multiplicative**.
|
||||
|
||||
|
|
@ -94,12 +94,12 @@ Consider the [differences](https://www.youtube.com/watch?v=oyLBGkS5ICk) between
|
|||
|
||||
So for `n` randomly sampled signals matching some criteria, you'd have seen an estimated `Σ(1.0/sample-rate_i)` such signals _without_ sampling, etc.
|
||||
|
||||
- Middleware can return any type, but it's best to return only `nil` or a map. This ensures maximum compatibility with community middleware, handlers, and tools.
|
||||
- Transforms can technically return any type, but it's best to return only `nil` or a map. This ensures maximum compatibility with community transforms, handlers, and tools.
|
||||
|
||||
- Middleware can be used to **filter signals** by returning `nil`.
|
||||
- Middleware can be used to **split signals**:
|
||||
- Transforms can be used to **filter signals** by returning `nil`.
|
||||
- Transforms can be used to **split signals**:
|
||||
|
||||
Your middleware can *call signal creators* like any other code. Return `nil` after to filter the source signal. Just be aware that new signals will re-enter your handler queue/s as would any other signal - and so may be subject to handling delay and normal handler queue back-pressure.
|
||||
Your transforms can *call signal creators* like any other code. Return `nil` after to filter the source signal. Just be aware that new signals will re-enter your handler queue/s as would any other signal - and so may be subject to handling delay and normal handler queue back-pressure.
|
||||
|
||||
See also the [`dispatch-signal!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#dispatch-signal!) util.
|
||||
|
||||
|
|
@ -122,7 +122,7 @@ Consider the [differences](https://www.youtube.com/watch?v=oyLBGkS5ICk) between
|
|||
|
||||
Note that all app-level kvs will *also* be available *together* under the signal's `:kvs` key.
|
||||
|
||||
App-level kvs are typically *not* included in handler output, so are a great way of providing custom data/opts for use (only) by custom middleware or handlers.
|
||||
App-level kvs are typically *not* included in handler output, so are a great way of providing custom data/opts for use (only) by custom transforms or handlers.
|
||||
|
||||
- Signal `kind` can be useful in advanced cases.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
My plan for Telemere is to offer a **stable core of limited scope**, then to focus on making it as easy for the **community** to write additional stuff like handlers, middleware, and utils.
|
||||
My plan for Telemere is to offer a **stable core of limited scope**, then to focus on making it as easy for the **community** to write additional stuff like handlers, transforms, and utils.
|
||||
|
||||
**PRs very welcome** to add links to this page!
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ Includes videos, tutorials, demo projects, etc.
|
|||
|
||||
# Handlers and tools
|
||||
|
||||
Includes libraries or examples for handlers (see [Writing handlers](./4-Handlers#writing-handlers)), middleware, handler utils (e.g. formatters), tools for analyzing signals, etc. [PRs](../wiki#contributions-welcome) welcome for additions!
|
||||
Includes libraries or examples for handlers (see [Writing handlers](./4-Handlers#writing-handlers)), transforms, handler utils (e.g. formatters), tools for analyzing signals, etc. [PRs](../wiki#contributions-welcome) welcome for additions!
|
||||
|
||||
| Type | Description |
|
||||
| ---- | :------------------------------------------------------------ |
|
||||
|
|
|
|||
Loading…
Reference in a new issue