[mod] Improve SLF4J, tools.logging interop signals

Incl.:

1. Logger names are now used as namespaces.
   - For SLF4J:         these are typically class names.
   - For tools.logging: these are typically *ns* strings.

2. These now have dedicated :kind (:slf4j, :tools.logging) to make it
   easier for users to set kind-specific min levels.
This commit is contained in:
Peter Taoussanis 2024-05-25 00:23:35 +02:00
parent 8f1035ff97
commit 22c46afa04
5 changed files with 47 additions and 52 deletions

View file

@ -6,7 +6,7 @@ Default signal keys:
`:schema` ------ Int version of signal schema (current: 1) `:schema` ------ Int version of signal schema (current: 1)
`:inst` -------- Platform instant [1] when signal was created `:inst` -------- Platform instant [1] when signal was created
`:level` ------- Signal level ∈ #{<int> :trace :debug :info :warn :error :fatal :report ...} `:level` ------- Signal level ∈ #{<int> :trace :debug :info :warn :error :fatal :report ...}
`:kind` -------- Signal ?kind ∈ #{nil :event :error :log :trace :spy <user-val> ...} `:kind` -------- Signal ?kind ∈ #{nil :event :error :log :trace :spy :slf4j :tools-logging <user-val> ...}
`:id` ---------- ?id of signal (common to all signals created at callsite, contrast with `:uid`) `:id` ---------- ?id of signal (common to all signals created at callsite, contrast with `:uid`)
`:uid` --------- ?id of signal instance (unique to each signal created at callsite, contrast with `:id`) `:uid` --------- ?id of signal instance (unique to each signal created at callsite, contrast with `:id`)

View file

@ -49,30 +49,30 @@ public class TelemereLogger extends LegacyAbstractLogger implements LoggingEvent
} }
private static IFn logFn; private static IFn logFn;
private static IFn isLevelEnabledFn; private static IFn isAllowedFn;
static void init() { static void init() {
IFn requireFn = Clojure.var("clojure.core", "require"); IFn requireFn = Clojure.var("clojure.core", "require");
requireFn.invoke( Clojure.read("taoensso.telemere.slf4j")); requireFn.invoke(Clojure.read("taoensso.telemere.slf4j"));
isAllowedFn = Clojure.var("taoensso.telemere.slf4j", "allowed?");
logFn = Clojure.var("taoensso.telemere.slf4j", "log!"); logFn = Clojure.var("taoensso.telemere.slf4j", "log!");
isLevelEnabledFn = Clojure.var("taoensso.telemere.slf4j", "allowed?");
} }
protected TelemereLogger(String name) { this.name = name; } protected TelemereLogger(String name) { this.name = name; }
protected boolean isLevelEnabled(Level level) { return (boolean) isLevelEnabledFn.invoke(level); } protected boolean isLevelEnabled(Level level) { return (boolean) isAllowedFn.invoke(this.name, level); }
public boolean isTraceEnabled() { return (boolean) isLevelEnabledFn.invoke(Level.TRACE); } public boolean isTraceEnabled() { return (boolean) isAllowedFn.invoke(this.name, Level.TRACE); }
public boolean isDebugEnabled() { return (boolean) isLevelEnabledFn.invoke(Level.DEBUG); } public boolean isDebugEnabled() { return (boolean) isAllowedFn.invoke(this.name, Level.DEBUG); }
public boolean isInfoEnabled() { return (boolean) isLevelEnabledFn.invoke(Level.INFO); } public boolean isInfoEnabled() { return (boolean) isAllowedFn.invoke(this.name, Level.INFO); }
public boolean isWarnEnabled() { return (boolean) isLevelEnabledFn.invoke(Level.WARN); } public boolean isWarnEnabled() { return (boolean) isAllowedFn.invoke(this.name, Level.WARN); }
public boolean isErrorEnabled() { return (boolean) isLevelEnabledFn.invoke(Level.ERROR); } public boolean isErrorEnabled() { return (boolean) isAllowedFn.invoke(this.name, Level.ERROR); }
public void log(LoggingEvent event) { logFn.invoke(event); } // Fluent (modern) API, called after level check public void log(LoggingEvent event) { logFn.invoke(this.name, event); } // Fluent (modern) API, called after level check
@Override protected String getFullyQualifiedCallerName() { return null; } @Override protected String getFullyQualifiedCallerName() { return null; }
@Override @Override
protected void handleNormalizedLoggingCall(Level level, Marker marker, String messagePattern, Object[] arguments, Throwable throwable) { protected void handleNormalizedLoggingCall(Level level, Marker marker, String messagePattern, Object[] arguments, Throwable throwable) {
logFn.invoke(level, throwable, messagePattern, arguments, marker); // Legacy API, called after level check logFn.invoke(this.name, level, throwable, messagePattern, arguments, marker); // Legacy API, called after level check
} }
} }

View file

@ -102,22 +102,20 @@
(defn- allowed? (defn- allowed?
"Private, don't use. "Private, don't use.
Called by `com.taoensso.telemere.slf4j.TelemereLogger`." Called by `com.taoensso.telemere.slf4j.TelemereLogger`."
[^org.slf4j.event.Level level] [logger-name level]
(when-debug (println [:slf4j/allowed? (sig-level level)])) (when-debug (println [:slf4j/allowed? (sig-level level) logger-name]))
(impl/signal-allowed? (impl/signal-allowed?
{:location nil {:location {:ns logger-name} ; Typically source class name
:kind :log :kind :slf4j
:id :taoensso.telemere/slf4j
:level (sig-level level)})) :level (sig-level level)}))
(defn- normalized-log! (defn- normalized-log!
[inst level error msg-pattern args marker-names kvs] [logger-name level inst error msg-pattern args marker-names kvs]
(when-debug (println [:slf4j/normalized-log! (sig-level level)])) (when-debug (println [:slf4j/normalized-log! (sig-level level) logger-name]))
(impl/signal! (impl/signal!
{:allow? true ; Pre-filtered by `allowed?` call {:allow? true ; Pre-filtered by `allowed?` call
:location nil :location {:ns logger-name} ; Typically source class name
:kind :log :kind :slf4j
:id :taoensso.telemere/slf4j
:level (sig-level level) :level (sig-level level)
:inst inst :inst inst
:error error :error error
@ -143,7 +141,7 @@
Called by `com.taoensso.telemere.slf4j.TelemereLogger`." Called by `com.taoensso.telemere.slf4j.TelemereLogger`."
;; Modern "fluent" API calls ;; Modern "fluent" API calls
([^org.slf4j.event.LoggingEvent event] ([logger-name ^org.slf4j.event.LoggingEvent event]
(let [inst (or (when-let [ts (.getTimeStamp event)] (java.time.Instant/ofEpochMilli ts)) (enc/now-inst*)) (let [inst (or (when-let [ts (.getTimeStamp event)] (java.time.Instant/ofEpochMilli ts)) (enc/now-inst*))
level (.getLevel event) level (.getLevel event)
error (.getThrowable event) error (.getThrowable event)
@ -156,17 +154,17 @@
(assoc acc (.-key kvp) (.-value kvp))) (assoc acc (.-key kvp) (.-value kvp)))
nil kvps))] nil kvps))]
(when-debug (println [:slf4j/fluent-log-call (sig-level level)])) (when-debug (println [:slf4j/fluent-log-call (sig-level level) logger-name]))
(normalized-log! inst level error msg-pattern args markers kvs))) (normalized-log! logger-name level inst error msg-pattern args markers kvs)))
;; Legacy API calls ;; Legacy API calls
([^org.slf4j.event.Level level error msg-pattern args marker] ([logger-name ^org.slf4j.event.Level level error msg-pattern args marker]
(let [marker-names (when marker (marker-names marker))] (let [marker-names (when marker (marker-names marker))]
(when-debug (println [:slf4j/legacy-log-call (sig-level level)])) (when-debug (println [:slf4j/legacy-log-call (sig-level level) logger-name]))
(normalized-log! (enc/now-inst*) level error msg-pattern args marker-names nil)))) (normalized-log! logger-name level (enc/now-inst*) error msg-pattern args marker-names nil))))
(comment (comment
(def ^org.slf4j.Logger sl (org.slf4j.LoggerFactory/getLogger "MySlfLogger")) (def ^org.slf4j.Logger sl (org.slf4j.LoggerFactory/getLogger "my.class"))
(impl/with-signal (-> sl (.info "Hello {}" "x"))) (impl/with-signal (-> sl (.info "Hello {}" "x")))
(impl/with-signal (-> (.atInfo sl) (.log "Hello {}" "x"))) (impl/with-signal (-> (.atInfo sl) (.log "Hello {}" "x")))

View file

@ -14,24 +14,22 @@
(defmacro ^:private when-debug [& body] (when #_true false `(do ~@body))) (defmacro ^:private when-debug [& body] (when #_true false `(do ~@body)))
(deftype TelemereLogger [logger-ns] (deftype TelemereLogger [logger-name]
clojure.tools.logging.impl/Logger clojure.tools.logging.impl/Logger
(enabled? [_ level] (enabled? [_ level]
(when-debug (println [:tools-logging/enabled? logger-ns level])) (when-debug (println [:tools-logging/enabled? level logger-name]))
(impl/signal-allowed? (impl/signal-allowed?
{:location nil {:location {:ns logger-name} ; Typically *ns* string
:kind :log :kind :tools-logging
:id :taoensso.telemere/tools-logging
:level level})) :level level}))
(write! [_ level throwable message] (write! [_ level throwable message]
(when-debug (println [:tools-logging/write! logger-ns level])) (when-debug (println [:tools-logging/write! level logger-name]))
(impl/signal! (impl/signal!
{:allow? true ; Pre-filtered by `enabled?` call {:allow? true ; Pre-filtered by `enabled?` call
:location nil :location {:ns logger-name} ; Typically *ns* string
:kind :log :kind :tools-logging
:id :taoensso.telemere/tools-logging
:level level :level level
:error throwable :error throwable
:msg message}) :msg message})
@ -40,7 +38,7 @@
(deftype TelemereLoggerFactory [] (deftype TelemereLoggerFactory []
clojure.tools.logging.impl/LoggerFactory clojure.tools.logging.impl/LoggerFactory
(name [_ ] "taoensso.telemere") (name [_ ] "taoensso.telemere")
(get-logger [_ logger-ns] (TelemereLogger. (str logger-ns)))) (get-logger [_ logger-name] (TelemereLogger. (str logger-name))))
(defn tools-logging->telemere! (defn tools-logging->telemere!
"Configures `tools.logging` to use Telemere as its logging "Configures `tools.logging` to use Telemere as its logging

View file

@ -576,15 +576,14 @@
;;;; Intake ;;;; Intake
(comment (def ^org.slf4j.Logger sl (org.slf4j.LoggerFactory/getLogger "MyTelemereSLF4JLogger"))) (comment (def ^org.slf4j.Logger sl (org.slf4j.LoggerFactory/getLogger "my.class")))
#?(:clj #?(:clj
(deftest _intake (deftest _intake
[(testing "`tools.logging` -> Telemere" [(testing "`tools.logging` -> Telemere"
[(is (sm? (tel/check-intakes) {:tools-logging {:present? true, :sending->telemere? true, :telemere-receiving? true}})) [(is (sm? (tel/check-intakes) {:tools-logging {:present? true, :sending->telemere? true, :telemere-receiving? true}}))
(is (sm? (with-sig (ctl/info "Hello" "x" "y")) {:level :info, :ns "taoensso.telemere-tests", :kind :tools-logging, :msg_ "Hello x y", :inst pinst?}))
(is (sm? (with-sig (ctl/info "Hello" "x" "y")) {:level :info, :location nil, :ns nil, :kind :log, :id :taoensso.telemere/tools-logging, :msg_ "Hello x y", :inst pinst?})) (is (sm? (with-sig (ctl/warn "Hello" "x" "y")) {:level :warn, :ns "taoensso.telemere-tests", :kind :tools-logging, :msg_ "Hello x y", :inst pinst?}))
(is (sm? (with-sig (ctl/warn "Hello" "x" "y")) {:level :warn, :location nil, :ns nil, :kind :log, :id :taoensso.telemere/tools-logging, :msg_ "Hello x y", :inst pinst?}))
(is (sm? (with-sig (ctl/error ex1 "An error")) {:level :error, :error pex1?, :inst pinst?}) "Errors")]) (is (sm? (with-sig (ctl/error ex1 "An error")) {:level :error, :error pex1?, :inst pinst?}) "Errors")])
(testing "Standard out/err streams -> Telemere" (testing "Standard out/err streams -> Telemere"
@ -604,12 +603,12 @@
(testing "SLF4J -> Telemere" (testing "SLF4J -> Telemere"
[(is (sm? (tel/check-intakes) {:slf4j {:present? true, :sending->telemere? true, :telemere-receiving? true}})) [(is (sm? (tel/check-intakes) {:slf4j {:present? true, :sending->telemere? true, :telemere-receiving? true}}))
(let [^org.slf4j.Logger sl (org.slf4j.LoggerFactory/getLogger "MyTelemereSLF4JLogger")] (let [^org.slf4j.Logger sl (org.slf4j.LoggerFactory/getLogger "my.class")]
[(testing "Basics" [(testing "Basics"
[(is (sm? (with-sig (.info sl "Hello")) {:level :info, :location nil, :ns nil, :kind :log, :id :taoensso.telemere/slf4j, :msg_ "Hello", :inst pinst?}) "Legacy API: info basics") [(is (sm? (with-sig (.info sl "Hello")) {:level :info, :ns "my.class", :kind :slf4j, :msg_ "Hello", :inst pinst?}) "Legacy API: info basics")
(is (sm? (with-sig (.warn sl "Hello")) {:level :warn, :location nil, :ns nil, :kind :log, :id :taoensso.telemere/slf4j, :msg_ "Hello", :inst pinst?}) "Legacy API: warn basics") (is (sm? (with-sig (.warn sl "Hello")) {:level :warn, :ns "my.class", :kind :slf4j, :msg_ "Hello", :inst pinst?}) "Legacy API: warn basics")
(is (sm? (with-sig (-> (.atInfo sl) (.log "Hello"))) {:level :info, :location nil, :ns nil, :kind :log, :id :taoensso.telemere/slf4j, :msg_ "Hello", :inst pinst?}) "Fluent API: info basics") (is (sm? (with-sig (-> (.atInfo sl) (.log "Hello"))) {:level :info, :ns "my.class", :kind :slf4j, :msg_ "Hello", :inst pinst?}) "Fluent API: info basics")
(is (sm? (with-sig (-> (.atWarn sl) (.log "Hello"))) {:level :warn, :location nil, :ns nil, :kind :log, :id :taoensso.telemere/slf4j, :msg_ "Hello", :inst pinst?}) "Fluent API: warn basics")]) (is (sm? (with-sig (-> (.atWarn sl) (.log "Hello"))) {:level :warn, :ns "my.class", :kind :slf4j, :msg_ "Hello", :inst pinst?}) "Fluent API: warn basics")])
(testing "Message formatting" (testing "Message formatting"
(let [msgp "X is {} and Y is {}", expected {:msg_ "X is x and Y is y", :data {:slf4j/args ["x" "y"]}}] (let [msgp "X is {} and Y is {}", expected {:msg_ "X is x and Y is y", :data {:slf4j/args ["x" "y"]}}]