[new] Add signal-allowed? util

Useful for using Telemere's filtering features without generating
any signals, etc.
This commit is contained in:
Peter Taoussanis 2024-09-12 11:07:44 +02:00
parent a9005e7f1c
commit d12b0b145b
4 changed files with 118 additions and 31 deletions

View file

@ -68,6 +68,7 @@
#?(:clj impl/with-signal)
#?(:clj impl/with-signals)
#?(:clj impl/signal!)
#?(:clj impl/signal-allowed?)
;; Utils
utils/format-signal-fn

View file

@ -381,6 +381,13 @@
sample-rate kind ns id level when rate-limit,
ctx parent root trace?, do let data msg error run & kvs]}])
:signal-allowed?
'([{:as opts :keys
[#_defaults #_elide? #_allow? #_expansion-id, ; Undocumented
elidable? location #_location* #_inst #_uid #_middleware,
sample-rate kind ns id level when rate-limit,
#_ctx #_parent #_root #_trace?, #_do #_let #_data #_msg #_error #_run #_& #_kvs]}])
:event! ; [id] [id level-or-opts] => allowed?
'([id ]
[id level]
@ -762,11 +769,27 @@
(signal! {:level :info, :run "run"}))))
#?(:clj
(defmacro signal-allowed?
"Used only for interop (tools.logging, SLF4J, etc.)."
{:arglists (signal-arglists :signal!)}
(defmacro ^:public signal-allowed?
"Returns true iff signal with given opts would meet filtering conditions:
(when (signal-allowed? {:level :warn, <...>}) (my-custom-code))
Allows you to use Telemere's rich filtering system for conditionally
executing arbitrary code. Also handy for batching multiple signals
under a single set of conditions (incl. rate-limiting, sampling, etc.):
;; Logs exactly 2 or 0 messages (never 1):
(when (signal-allowed? {:level :info, :sample-rate 0.5})
(log! {:allow? true} \"Message 1\")
(log! {:allow? true} \"Message 2\"))"
;; Used also for interop (tools.logging, SLF4J), etc.
{:arglists (signal-arglists :signal-allowed?)}
[opts]
(let [{:keys [#_expansion-id #_location elide? allow?]}
(have? map? opts)
(let [defaults (get opts :defaults)
opts (merge defaults (dissoc opts :defaults))
{:keys [#_expansion-id #_location elide? allow?]}
(sigs/filterable-expansion
{:sf-arity 4
:ct-sig-filter ct-sig-filter
@ -774,7 +797,9 @@
(assoc opts :location*
(get opts :location* (enc/get-source &form &env))))]
(and (not elide?) allow?))))
(if elide? false `(if ~allow? true false)))))
(comment (macroexpand '(signal-allowed? {:level :info})))
;;;; Interop

View file

@ -275,7 +275,10 @@
[(is (= sv1 (read-string (pr-str sv1))))])))
(is (sm? (with-sig (shell/signal! {:level :info})) {:level :info, :ns "taoensso.telemere-tests", :line :submap/some}) "Shell API")])
(testing "Shell API"
[(is (sm? (with-sig (shell/signal! {:level :info})) {:level :info, :ns "taoensso.telemere-tests", :line :submap/some}))
(is (true? (tel/with-min-level :debug (shell/signal-allowed? {:level :debug}))))
(is (false? (tel/with-min-level :debug (shell/signal-allowed? {:level :trace}))))])])
(deftest _handlers
;; Basic handler tests are in Encore

View file

@ -12,13 +12,58 @@
(remove-ns 'taoensso.telemere.shell)
(:api (enc/interns-overview)))
;;;; Private
#?(:clj
(defmacro ^:private compile-if [test then else]
(if (try (eval test) (catch Throwable _ false)) then else)))
(def ^:private telemere-present?
"Is Telemere present (not necessarily loaded)?"
(compile-if (jio/resource "taoensso/telemere.cljc") true false))
#?(:clj
(def ^:private telemere-present?
"Is Telemere present (not necessarily loaded)?"
(compile-if (jio/resource "taoensso/telemere.cljc") true false)))
#?(:clj
(defn- require-telemere! []
(try
(require 'taoensso.telemere) ; For macro expansion
(catch Exception e
(throw
(ex-info "Failed to require `taoensso.telemere` - `(require-telemere-if-present)` call missing?"
{} e))))))
#?(:clj
(defn- get-source "From Encore" [macro-form macro-env]
(let [{:keys [line column file]} (meta macro-form)
file
(if-not (:ns macro-env)
*file* ; Compiling Clj
(or ; Compiling Cljs
(when-let [url (and file (try (jio/resource file) (catch Exception _)))]
(try (.getPath (jio/file url)) (catch Exception _))
(do (str url)))
file))
file
(when (string? file)
(when-not (contains? #{"NO_SOURCE_PATH" "NO_SOURCE_FILE" ""} file)
file))
m {:ns (str *ns*)}
m (if line (assoc m :line line) m)
m (if column (assoc m :column column) m)
m (if file (assoc m :file file) m)]
m)))
#?(:clj
(defn- signal-opts [macro-form macro-env opts]
(if (map? opts)
(conj {:location* (get-source macro-form macro-env)} (dissoc opts :fallback))
(throw
(ex-info "Signal opts must be a map"
{:given {:value opts, :type (type opts)}})))))
;;;; Public
#?(:clj
(defmacro if-telemere
@ -92,28 +137,12 @@
ctx parent root trace?, do let data msg error run & kvs]}])}
[opts]
(if (map? opts)
(if telemere-present?
(do
(try
(require 'taoensso.telemere) ; For macro expansion
(catch Exception e
(throw
(ex-info "Failed to require `taoensso.telemere` - `(require-telemere-if-present)` call missing?"
{} e))))
(with-meta ; Keep callsite
`(taoensso.telemere/signal! ~(dissoc opts :fallback))
(meta &form)))
(let [fb-form (get opts :fallback)]
(if-let [run-form (get opts :run)]
`(let [run-result# ~run-form] ~fb-form run-result#)
(do fb-form))))
(throw
(ex-info "`signal!` expects map opts"
{:given {:value opts, :type (type opts)}})))))
(if telemere-present?
(do (require-telemere!) `(taoensso.telemere/signal! ~(signal-opts &form &env opts)))
(let [fb-form (get opts :fallback)]
(if-let [run-form (get opts :run)]
`(let [run-result# ~run-form] ~fb-form run-result#)
(do fb-form))))))
(comment
(macroexpand
@ -123,3 +152,32 @@
:msg ["Hello" "world" x]
:data {:a :A :x x}
:fallback (println (str "Hello world " x))})))
#?(:clj
(defmacro signal-allowed?
"Experimental, subject to change.
Returns true iff Telemere is present and signal with given opts would meet
filtering conditions.
MUST be used with `require-telemere-if-present`, example:
(ns my-lib (:require [taoensso.telemere.shell :as t]))
(t/require-telemere-if-present) ; Just below `ns` form!
(when (t/signal-allowed? {:level :warn, <...>})
(my-custom-code))"
{:arglists
'([{:as opts :keys
[#_fallback, ; Unique to shell
#_defaults #_elide? #_allow? #_expansion-id, ; Undocumented
elidable? location #_location* #_inst #_uid #_middleware,
sample-rate kind ns id level when rate-limit,
#_ctx #_parent #_root #_trace?, #_do #_let #_data #_msg #_error #_run #_& #_kvs]}])}
[opts]
(if telemere-present?
(do (require-telemere!) `(taoensso.telemere/signal-allowed? ~(signal-opts &form &env opts)))
(get opts :fallback nil))))
(comment (macroexpand '(signal-allowed? {:level :warn, :sample-rate 0.5})))