From 722c6abb676fe6f70abc86f1bfdfdb948bbe46ba Mon Sep 17 00:00:00 2001 From: Peter Taoussanis Date: Tue, 5 Mar 2024 12:49:08 +0100 Subject: [PATCH] [new] Refactor interop checks, make extensible --- slf4j/src/taoensso/telemere/slf4j.clj | 13 ++++ src/taoensso/telemere.cljc | 85 ++++++------------------- src/taoensso/telemere/impl.cljc | 29 +++++++++ src/taoensso/telemere/streams.clj | 31 +++++---- src/taoensso/telemere/tools_logging.clj | 12 ++++ test/taoensso/telemere_tests.cljc | 16 +++-- 6 files changed, 101 insertions(+), 85 deletions(-) diff --git a/slf4j/src/taoensso/telemere/slf4j.clj b/slf4j/src/taoensso/telemere/slf4j.clj index 9679935..926f092 100644 --- a/slf4j/src/taoensso/telemere/slf4j.clj +++ b/slf4j/src/taoensso/telemere/slf4j.clj @@ -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?}))) diff --git a/src/taoensso/telemere.cljc b/src/taoensso/telemere.cljc index fcd6827..c44bd35 100644 --- a/src/taoensso/telemere.cljc +++ b/src/taoensso/telemere.cljc @@ -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 diff --git a/src/taoensso/telemere/impl.cljc b/src/taoensso/telemere/impl.cljc index c650da4..d7e9d3f 100644 --- a/src/taoensso/telemere/impl.cljc +++ b/src/taoensso/telemere/impl.cljc @@ -545,3 +545,32 @@ opts)] (and (not elide?) allow?)))) + +;;;; Interop + +(enc/defonce ^:private interop-checks_ "{ (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 + { {: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))) diff --git a/src/taoensso/telemere/streams.clj b/src/taoensso/telemere/streams.clj index d3e12fd..7fc459e 100644 --- a/src/taoensso/telemere/streams.clj +++ b/src/taoensso/telemere/streams.clj @@ -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?}))) diff --git a/src/taoensso/telemere/tools_logging.clj b/src/taoensso/telemere/tools_logging.clj index f049546..302cce9 100644 --- a/src/taoensso/telemere/tools_logging.clj +++ b/src/taoensso/telemere/tools_logging.clj @@ -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?}))) diff --git a/test/taoensso/telemere_tests.cljc b/test/taoensso/telemere_tests.cljc index 02595a3..85278d6 100644 --- a/test/taoensso/telemere_tests.cljc +++ b/test/taoensso/telemere_tests.cljc @@ -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")