[new] Refactor interop checks, make extensible

This commit is contained in:
Peter Taoussanis 2024-03-05 12:49:08 +01:00
parent 450798832c
commit 722c6abb67
6 changed files with 101 additions and 85 deletions

View file

@ -173,3 +173,16 @@
(org.slf4j.MDC/get "key")
(org.slf4j.MDC/getCopyOfContextMap)
(org.slf4j.MDC/clear)))
(impl/add-interop-check! :slf4j
(fn []
(let [^org.slf4j.Logger sl
(org.slf4j.LoggerFactory/getLogger "InteropTestTelemereLogger")
sending? (instance? com.taoensso.telemere.slf4j.TelemereLogger sl)
receiving?
(and sending?
(impl/test-interop! "SLF4J -> Telemere" #(.info sl %)))]
{:present? true
:sending->telemere? sending?
:telemere-receiving? receiving?})))

View file

@ -359,77 +359,28 @@
;;;; Interop
(enc/defaliases impl/check-interop)
#?(:clj
(do
(def ^:private have-tools-logging?
(enc/compile-if
(do (require '[taoensso.telemere.tools-logging :as ttl]) true)
true false))
(enc/defaliases
streams/with-out->telemere
streams/with-err->telemere
streams/with-streams->telemere
streams/streams->telemere!
streams/streams->reset!))
(enc/compile-when have-tools-logging?
(enc/defalias ttl/tools-logging->telemere!)
(when (enc/get-env {:as :bool} :clojure.tools.logging->telemere?)
(ttl/tools-logging->telemere!)))
#?(:clj
(enc/compile-when
(do (require '[taoensso.telemere.tools-logging :as ttl]) true)
(enc/defaliases ttl/tools-logging->telemere!)
(when (enc/get-env {:as :bool} :clojure.tools.logging->telemere?)
(ttl/tools-logging->telemere!))))
(enc/defaliases
streams/with-out->telemere
streams/with-err->telemere
streams/with-streams->telemere
streams/streams->telemere!
streams/streams->reset!)
#?(:clj
(enc/compile-when
(and org.slf4j.Logger com.taoensso.telemere.slf4j.TelemereLogger)
(require '[taoensso.telemere.slf4j :as slf4j])))
(defn- interop-test! [msg form-fn]
(let [msg (str "Interop test: " msg " (" (enc/uuid-str) ")")
signal
(without-filters
(impl/with-signal {:stop-propagation? true, :return :signal}
(form-fn msg)))]
(= (force (get signal :msg_)) msg)))
(defn interop-check
"Tests Telemere's interop with `clojure.tools.logging` and SLF4J, useful
for tests/debugging. Returns {:keys [tools-logging slf4j streams]} with
{:keys [send->telemere? receiving? ...]} sub-maps."
[]
(let [base-present {:present? true, :send->telemere? false, :receiving? false}]
{:tools-logging
(if-not (enc/have-resource? "clojure/tools/logging.clj")
{:present? false}
(merge base-present
(enc/compile-when have-tools-logging?
(let [sending? (ttl/tools-logging->telemere?)]
{:send->telemere? sending?
:receiving? (and sending?
(interop-test! "`clojure.tools.logging` -> Telemere"
#(clojure.tools.logging/info %)))}))))
:slf4j
(if-not (enc/have-class? "org.slf4j.Logger")
{:present? false}
(merge base-present
(enc/compile-when
(and org.slf4j.Logger com.taoensso.telemere.slf4j.TelemereLogger)
(let [^org.slf4j.Logger sl
(org.slf4j.LoggerFactory/getLogger "InteropTestTelemereLogger")
sending? (instance? com.taoensso.telemere.slf4j.TelemereLogger sl)]
{:send->telemere? sending?
:receiving? (and sending? (interop-test! "SLF4J -> Telemere" #(.info sl %)))}))))
:streams
{:out
(let [sending? (boolean @streams/orig-out_)]
{:send->telemere? sending?
:receiving? (and sending? (interop-test! "`System/out` -> Telemere" #(.println System/out %)))})
:err
(let [sending? (boolean @streams/orig-err_)]
{:send->telemere? sending?
:receiving? (and sending? (interop-test! "`System/err` -> Telemere" #(.println System/err %)))})}}))))
(comment (interop-check))
(comment (check-interop))
;;;; Flow benchmarks

View file

@ -545,3 +545,32 @@
opts)]
(and (not elide?) allow?))))
;;;; Interop
(enc/defonce ^:private interop-checks_ "{<id> (fn check [])}" (atom nil))
(defn add-interop-check! [id check-fn] (swap! interop-checks_ assoc id check-fn))
#?(:clj
(when (nil? @interop-checks_)
(add-interop-check! :tools-logging (fn [] {:present? (enc/have-resource? "clojure/tools/logging.clj")}))
(add-interop-check! :slf4j (fn [] {:present? (enc/compile-when org.slf4j.Logger true false)}))))
(defn ^:public check-interop
"Experimental, subject to change.
Runs Telemere's registered interop checks and returns
{<interop-id> {:keys [sending->telemere? telemere-receiving? ...]}}.
Useful for tests/debugging."
[]
(enc/map-vals (fn [check-fn] (check-fn))
@interop-checks_))
(defn test-interop! [msg test-fn]
(let [msg (str "Interop test: " msg " (" (enc/uuid-str) ")")
signal
(binding [*rt-sig-filter* nil] ; without runtime filters
(-with-signal (fn [] (test-fn msg))
{:stop-propagation? true, :return :signal}))]
(= (force (get signal :msg_)) msg)))

View file

@ -73,18 +73,11 @@
(comment (impl/with-signal (with-out->telemere (println "hello"))))
(enc/defonce orig-out_ "Original `System/out`, or nil" (atom nil))
(enc/defonce orig-err_ "Original `System/err`, or nil" (atom nil))
(enc/defonce ^:private orig-out_ "Original `System/out`, or nil" (atom nil))
(enc/defonce ^:private orig-err_ "Original `System/err`, or nil" (atom nil))
(let [monitor (Object.)]
#_
(defn streams->telemere? []
;; Not easy to actually identify current `System/out`, `System/err` vals
(locking monitor
{:out (boolean @orig-out_)
:err (boolean @orig-err_)}))
(defn ^:public streams->reset!
"Experimental, subject to change without notice!
Resets `System/out` and `System/err` to their original value (prior to any
@ -118,13 +111,25 @@
(let [out (when out (telemere-print-stream out))
err (when err (telemere-print-stream err))]
(locking monitor
(when out (compare-and-set! orig-out_ nil System/out) (System/setOut out))
(when err (compare-and-set! orig-err_ nil System/err) (System/setErr err)))
(locking monitor
(when out (compare-and-set! orig-out_ nil System/out) (System/setOut out))
(when err (compare-and-set! orig-err_ nil System/err) (System/setErr err)))
true)))))
true)))))
(comment
(streams->telemere?)
(streams->telemere! {})
(streams->reset!))
(impl/add-interop-check! :system/out
(fn []
(let [sending? (boolean @orig-out_)
receiving? (and sending? (impl/test-interop! "`System/out` -> Telemere" #(.println System/out %)))]
{:sending->telemere? sending?, :telemere-receiving? receiving?})))
(impl/add-interop-check! :system/err
(fn []
(let [sending? (boolean @orig-err_)
receiving? (and sending? (impl/test-interop! "`System/err` -> Telemere" #(.println System/err %)))]
{:sending->telemere? sending?, :telemere-receiving? receiving?})))

View file

@ -49,3 +49,15 @@
(defn tools-logging->telemere? []
(when-let [lf clojure.tools.logging/*logger-factory*]
(instance? TelemereLoggerFactory lf)))
(impl/add-interop-check! :tools-logging
(fn []
(let [sending? (tools-logging->telemere?)
receiving?
(and sending?
(impl/test-interop! "`clojure.tools.logging` -> Telemere"
#(clojure.tools.logging/info %)))]
{:present? true
:sending->telemere? sending?
:telemere-receiving? receiving?})))

View file

@ -470,23 +470,29 @@
#?(:clj
(deftest _interop
[(testing "`clojure.tools.logging` -> Telemere"
[(is (sm? (tel/interop-check) {:tools-logging {:present? true, :send->telemere? true, :receiving? true}}))
[(is (sm? (tel/check-interop) {:tools-logging {:present? true, :sending->telemere? true, :telemere-receiving? true}}))
(is (sm? (wsv (ctl/info "Hello" "x" "y")) {:level :info, :location nil, :ns nil, :kind :log, :id :taoensso.telemere/tools-logging, :msg_ "Hello x y"}))
(is (sm? (wsv (ctl/warn "Hello" "x" "y")) {:level :warn, :location nil, :ns nil, :kind :log, :id :taoensso.telemere/tools-logging, :msg_ "Hello x y"}))
(is (sm? (wsv (ctl/error ex1 "An error")) {:level :error, :error ex1}) "Errors")])
(testing "Standard out/err streams -> Telemere"
[(is (sm? (tel/interop-check) {:streams {:out {:send->telemere? false, :receiving? false}, :err {:send->telemere? false, :receiving? false}}}))
[(is (sm? (tel/check-interop) {:system/out {:sending->telemere? false, :telemere-receiving? false},
:system/err {:sending->telemere? false, :telemere-receiving? false}}))
(is (true? (tel/streams->telemere!)))
(is (sm? (tel/interop-check) {:streams {:out {:send->telemere? true, :receiving? true}, :err {:send->telemere? true, :receiving? true}}}))
(is (sm? (tel/check-interop) {:system/out {:sending->telemere? true, :telemere-receiving? true},
:system/err {:sending->telemere? true, :telemere-receiving? true}}))
(is (true? (tel/streams->reset!)))
(is (sm? (tel/interop-check) {:streams {:out {:send->telemere? false, :receiving? false}, :err {:send->telemere? false, :receiving? false}}}))
(is (sm? (tel/check-interop) {:system/out {:sending->telemere? false, :telemere-receiving? false},
:system/err {:sending->telemere? false, :telemere-receiving? false}}))
(is
(sm? (wsv (tel/with-out->telemere (println "Hello" "x" "y")))
{:level :info, :location nil, :ns nil, :kind :system/out, :msg_ "Hello x y"}))])
(testing "SLF4J -> Telemere"
[(is (sm? (tel/interop-check) {:slf4j {:present? true, :send->telemere? true, :receiving? true}}))
[(is (sm? (tel/check-interop) {:slf4j {:present? true, :sending->telemere? true, :telemere-receiving? true}}))
(let [^org.slf4j.Logger sl (org.slf4j.LoggerFactory/getLogger "MyTelemereSLF4JLogger")]
[(testing "Basics"
[(is (sm? (wsv (.info sl "Hello")) {:level :info, :location nil, :ns nil, :kind :log, :id :taoensso.telemere/slf4j, :msg_ "Hello"}) "Legacy API: info basics")