[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)
`:inst` -------- Platform instant [1] when signal was created
`: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`)
`: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 isLevelEnabledFn;
private static IFn isAllowedFn;
static void init() {
IFn requireFn = Clojure.var("clojure.core", "require");
requireFn.invoke( Clojure.read("taoensso.telemere.slf4j"));
logFn = Clojure.var("taoensso.telemere.slf4j", "log!");
isLevelEnabledFn = Clojure.var("taoensso.telemere.slf4j", "allowed?");
IFn requireFn = Clojure.var("clojure.core", "require");
requireFn.invoke(Clojure.read("taoensso.telemere.slf4j"));
isAllowedFn = Clojure.var("taoensso.telemere.slf4j", "allowed?");
logFn = Clojure.var("taoensso.telemere.slf4j", "log!");
}
protected TelemereLogger(String name) { this.name = name; }
protected boolean isLevelEnabled(Level level) { return (boolean) isLevelEnabledFn.invoke(level); }
public boolean isTraceEnabled() { return (boolean) isLevelEnabledFn.invoke(Level.TRACE); }
public boolean isDebugEnabled() { return (boolean) isLevelEnabledFn.invoke(Level.DEBUG); }
public boolean isInfoEnabled() { return (boolean) isLevelEnabledFn.invoke(Level.INFO); }
public boolean isWarnEnabled() { return (boolean) isLevelEnabledFn.invoke(Level.WARN); }
public boolean isErrorEnabled() { return (boolean) isLevelEnabledFn.invoke(Level.ERROR); }
protected boolean isLevelEnabled(Level level) { return (boolean) isAllowedFn.invoke(this.name, level); }
public boolean isTraceEnabled() { return (boolean) isAllowedFn.invoke(this.name, Level.TRACE); }
public boolean isDebugEnabled() { return (boolean) isAllowedFn.invoke(this.name, Level.DEBUG); }
public boolean isInfoEnabled() { return (boolean) isAllowedFn.invoke(this.name, Level.INFO); }
public boolean isWarnEnabled() { return (boolean) isAllowedFn.invoke(this.name, Level.WARN); }
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 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?
"Private, don't use.
Called by `com.taoensso.telemere.slf4j.TelemereLogger`."
[^org.slf4j.event.Level level]
(when-debug (println [:slf4j/allowed? (sig-level level)]))
[logger-name level]
(when-debug (println [:slf4j/allowed? (sig-level level) logger-name]))
(impl/signal-allowed?
{:location nil
:kind :log
:id :taoensso.telemere/slf4j
{:location {:ns logger-name} ; Typically source class name
:kind :slf4j
:level (sig-level level)}))
(defn- normalized-log!
[inst level error msg-pattern args marker-names kvs]
(when-debug (println [:slf4j/normalized-log! (sig-level level)]))
[logger-name level inst error msg-pattern args marker-names kvs]
(when-debug (println [:slf4j/normalized-log! (sig-level level) logger-name]))
(impl/signal!
{:allow? true ; Pre-filtered by `allowed?` call
:location nil
:kind :log
:id :taoensso.telemere/slf4j
:location {:ns logger-name} ; Typically source class name
:kind :slf4j
:level (sig-level level)
:inst inst
:error error
@ -143,7 +141,7 @@
Called by `com.taoensso.telemere.slf4j.TelemereLogger`."
;; 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*))
level (.getLevel event)
error (.getThrowable event)
@ -156,17 +154,17 @@
(assoc acc (.-key kvp) (.-value kvp)))
nil kvps))]
(when-debug (println [:slf4j/fluent-log-call (sig-level level)]))
(normalized-log! inst level error msg-pattern args markers kvs)))
(when-debug (println [:slf4j/fluent-log-call (sig-level level) logger-name]))
(normalized-log! logger-name level inst error msg-pattern args markers kvs)))
;; 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))]
(when-debug (println [:slf4j/legacy-log-call (sig-level level)]))
(normalized-log! (enc/now-inst*) level error msg-pattern args marker-names nil))))
(when-debug (println [:slf4j/legacy-log-call (sig-level level) logger-name]))
(normalized-log! logger-name level (enc/now-inst*) error msg-pattern args marker-names nil))))
(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 (-> (.atInfo sl) (.log "Hello {}" "x")))
@ -182,7 +180,7 @@
"Returns {:keys [present? sending->telemere? telemere-receiving?]}."
[]
(let [^org.slf4j.Logger sl
(org.slf4j.LoggerFactory/getLogger "IntakeTestTelemereLogger")
(org.slf4j.LoggerFactory/getLogger "IntakeTestTelemereLogger")
sending? (instance? com.taoensso.telemere.slf4j.TelemereLogger sl)
receiving?
(and sending?

View file

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

View file

@ -576,15 +576,14 @@
;;;; 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
(deftest _intake
[(testing "`tools.logging` -> Telemere"
[(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, :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, :location nil, :ns nil, :kind :log, :id :taoensso.telemere/tools-logging, :msg_ "Hello x y", :inst pinst?}))
(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/warn "Hello" "x" "y")) {:level :warn, :ns "taoensso.telemere-tests", :kind :tools-logging, :msg_ "Hello x y", :inst pinst?}))
(is (sm? (with-sig (ctl/error ex1 "An error")) {:level :error, :error pex1?, :inst pinst?}) "Errors")])
(testing "Standard out/err streams -> Telemere"
@ -604,12 +603,12 @@
(testing "SLF4J -> Telemere"
[(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"
[(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 (.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 (-> (.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 (-> (.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 (.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, :ns "my.class", :kind :slf4j, :msg_ "Hello", :inst pinst?}) "Legacy API: warn 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, :ns "my.class", :kind :slf4j, :msg_ "Hello", :inst pinst?}) "Fluent API: warn basics")])
(testing "Message formatting"
(let [msgp "X is {} and Y is {}", expected {:msg_ "X is x and Y is y", :data {:slf4j/args ["x" "y"]}}]