Expose an official low-level logging API for tools, etc.

This commit is contained in:
Peter Taoussanis 2016-01-26 14:53:05 +07:00
parent e611f7a584
commit e9e20ef88e
2 changed files with 102 additions and 63 deletions

View File

@ -202,6 +202,7 @@
"(fn [whitelist blacklist ns]) -> ?unfiltered-ns" "(fn [whitelist blacklist ns]) -> ?unfiltered-ns"
(enc/memoize_ (enc/memoize_
(fn [whitelist blacklist ns] (fn [whitelist blacklist ns]
{:pre [(have? string? ns)]}
((compile-ns-filters whitelist blacklist) ns)))) ((compile-ns-filters whitelist blacklist) ns))))
(comment (qb 10000 (ns-filter ["foo.*"] ["foo.baz"] "foo.bar"))) (comment (qb 10000 (ns-filter ["foo.*"] ["foo.baz"] "foo.bar")))
@ -218,7 +219,7 @@
(when blacklist (when blacklist
(println (str "Compile-time (elision) Timbre ns blacklist: " blacklist))) (println (str "Compile-time (elision) Timbre ns blacklist: " blacklist)))
(partial ns-filter whitelist blacklist))) (fn [ns] (ns-filter whitelist blacklist ns))))
;;;; Utils ;;;; Utils
@ -273,15 +274,18 @@
;;;; Internal logging core ;;;; Internal logging core
(defn log? (defn log?
"Would Timbre currently log at the given logging level? "Would Timbre currently (runtime) log at the given logging level?
* Compile-time `?ns-str` arg required to support ns filtering. * `?ns-str` arg required to support ns filtering
* `config` arg required to support non-global config." * `config` arg required to support non-global config"
[level & [?ns-str config]] ([level ] (log? level nil nil))
(let [config (or config *config*) ([level ?ns-str ] (log? level ?ns-str nil))
active-level (or (:level config) :report)] ([level ?ns-str config]
(and (level>= level active-level) (let [config (or config *config*)
(ns-filter (:ns-whitelist config) (:ns-blacklist config) (or ?ns-str "")) active-level (or (:level config) :report)]
true))) (and
(level>= level active-level)
(ns-filter (:ns-whitelist config) (:ns-blacklist config) (or ?ns-str ""))
true))))
(comment (comment
(set-level! :debug) (set-level! :debug)
@ -306,6 +310,7 @@
(def ^:dynamic *context* (def ^:dynamic *context*
"General-purpose dynamic logging context. Context will be included in appender "General-purpose dynamic logging context. Context will be included in appender
data map at logging time." nil) data map at logging time." nil)
(defmacro with-context [context & body] `(binding [*context* ~context] ~@body)) (defmacro with-context [context & body] `(binding [*context* ~context] ~@body))
(declare get-hostname) (declare get-hostname)
@ -325,14 +330,18 @@
(inherit-over :foo {:foo :inherit} {:foo :bar} nil) (inherit-over :foo {:foo :inherit} {:foo :bar} nil)
(inherit-into :foo {:foo {:a :A :b :B :c :C}} {:foo {:a 1 :b 2 :c 3 :d 4}} nil)) (inherit-into :foo {:foo {:a :A :b :B :c :C}} {:foo {:a 1 :b 2 :c 3 :d 4}} nil))
(defn log1-fn (defn -log! "Core low-level log fn. Implementation detail."
"Core fn-level logger. Implementation detail!" [config level ?ns-str ?file ?line msg-type ?err vargs_ ?base-data]
[config level ?ns-str ?file ?line msg-type vargs_ ?base-data] (when (log? level ?ns-str config) ; Runtime check
(when (log? level ?ns-str config) (let [instant (enc/now-dt)
(let [instant (enc/now-dt) ;; vargs_ (->delay vargs_)
vargs*_ (delay (vsplit-err1 @vargs_))
?err_ (delay (get @vargs*_ 0)) ;; Extract ?err as err-type a0 in vargs?:
vargs_ (delay (get @vargs*_ 1)) a0-err? (enc/kw-identical? ?err :auto)
vargs*_ (if a0-err? (delay (vsplit-err1 @vargs_)) vargs_)
?err_ (if a0-err? (delay (get @vargs*_ 0)) (delay ?err))
vargs_ (if a0-err? (delay (get @vargs*_ 1)) vargs_)
context *context* context *context*
data (merge ?base-data data (merge ?base-data
;; No, better nest than merge (appenders may want to pass ;; No, better nest than merge (appenders may want to pass
@ -427,49 +436,76 @@
nil) nil)
(comment (comment
(log1-fn *config* :info nil nil nil :p (delay [(do (println "hi") :x) :y]) nil)) (-log! *config* :info nil nil nil :p :auto
(delay [(do (println "hi") :x) :y]) nil))
(defmacro log1-macro (defmacro -with-elision
"Core macro-level logger. Implementation detail!" "Implementation detail.
[config level msg-type args & [?base-data]] Executes body iff given level and ns pass compile-time elision."
[level-form ns-str-form & body]
;; Compile-time elision:
(when (or (nil? compile-time-level) (when (or (nil? compile-time-level)
(not (valid-levels level)) ; Not a compile-time level (not (valid-levels level-form)) ; Not a compile-time level const
(level>= level compile-time-level)) (level>= level-form compile-time-level))
(when (compile-time-ns-filter (str *ns*)) (when (or (not (string? ns-str-form)) ; Not a compile-time ns-str const
(compile-time-ns-filter ns-str-form))
`(do ~@body))))
(let [ns-str (str *ns*) (comment (-with-elision :info "ns" (println "foo")))
?file (let [f *file*] (when (not= f "NO_SOURCE_PATH") f))
;; TODO Waiting on http://dev.clojure.org/jira/browse/CLJ-865: (defmacro log! ; Public wrapper around `-log!`
?line (:line (meta &form))] "Core low-level log macro. Useful for tooling, etc.
`(log1-fn ~config ~level ~ns-str ~?file ~?line ~msg-type
* `level` - must eval to a valid logging level
* `msg-type` - must eval to e/o #{:p :f nil}
* `opts` - ks e/o #{:config :?err :?ns-str :?file :?line
:?base-data}
Supports compile-time elision when compile-time const vals
provided for `level` and/or `?ns-str`."
[level msg-type args & [opts]]
(have sequential? args) ; To allow -> (delay [~@args])
(let [{:keys [?ns-str] :or {?ns-str (str *ns*)}} opts]
(-with-elision
level ; level-form (may/not be a compile-time kw const)
?ns-str ; ns-str-form (may/not be a compile-time str const)
(let [{:keys [config ?err ?file ?line ?base-data]
:or {config 'taoensso.timbre/*config*
?err :auto ; => Extract as err-type a0
?file (let [f *file*] (when (not= f "NO_SOURCE_PATH") f))
;; TODO Waiting on http://dev.clojure.org/jira/browse/CLJ-865:
?line (:line (meta &form))}} opts]
`(-log! ~config ~level ~?ns-str ~?file ~?line ~msg-type ~?err
(delay [~@args]) ~?base-data))))) (delay [~@args]) ~?base-data)))))
;;;; API-level stuff (comment
(log! :info :p ["foo"])
(macroexpand '(log! :info :p ["foo"]))
(macroexpand '(log! :info :p ["foo"] {:?line 42})))
;;;; Main public API-level stuff
;;; Log using print-style args ;;; Log using print-style args
(defmacro log* [config level & args] `(log1-macro ~config ~level :p ~args)) (defmacro log* [config level & args] `(log! ~level :p ~args {:config ~config}))
(defmacro log [level & args] `(log1-macro *config* ~level :p ~args)) (defmacro log [level & args] `(log! ~level :p ~args))
(defmacro trace [& args] `(log1-macro *config* :trace :p ~args)) (defmacro trace [& args] `(log! :trace :p ~args))
(defmacro debug [& args] `(log1-macro *config* :debug :p ~args)) (defmacro debug [& args] `(log! :debug :p ~args))
(defmacro info [& args] `(log1-macro *config* :info :p ~args)) (defmacro info [& args] `(log! :info :p ~args))
(defmacro warn [& args] `(log1-macro *config* :warn :p ~args)) (defmacro warn [& args] `(log! :warn :p ~args))
(defmacro error [& args] `(log1-macro *config* :error :p ~args)) (defmacro error [& args] `(log! :error :p ~args))
(defmacro fatal [& args] `(log1-macro *config* :fatal :p ~args)) (defmacro fatal [& args] `(log! :fatal :p ~args))
(defmacro report [& args] `(log1-macro *config* :report :p ~args)) (defmacro report [& args] `(log! :report :p ~args))
;;; Log using format-style args ;;; Log using format-style args
(defmacro logf* [config level & args] `(log1-macro ~config ~level :f ~args)) (defmacro logf* [config level & args] `(log! ~level :f ~args {:config ~config}))
(defmacro logf [level & args] `(log1-macro *config* ~level :f ~args)) (defmacro logf [level & args] `(log! ~level :f ~args))
(defmacro tracef [& args] `(log1-macro *config* :trace :f ~args)) (defmacro tracef [& args] `(log! :trace :f ~args))
(defmacro debugf [& args] `(log1-macro *config* :debug :f ~args)) (defmacro debugf [& args] `(log! :debug :f ~args))
(defmacro infof [& args] `(log1-macro *config* :info :f ~args)) (defmacro infof [& args] `(log! :info :f ~args))
(defmacro warnf [& args] `(log1-macro *config* :warn :f ~args)) (defmacro warnf [& args] `(log! :warn :f ~args))
(defmacro errorf [& args] `(log1-macro *config* :error :f ~args)) (defmacro errorf [& args] `(log! :error :f ~args))
(defmacro fatalf [& args] `(log1-macro *config* :fatal :f ~args)) (defmacro fatalf [& args] `(log! :fatal :f ~args))
(defmacro reportf [& args] `(log1-macro *config* :report :f ~args)) (defmacro reportf [& args] `(log! :report :f ~args))
(comment (comment
(infof "hello %s" "world") (infof "hello %s" "world")

View File

@ -1,27 +1,30 @@
(ns taoensso.timbre.tools.logging (ns taoensso.timbre.tools.logging
"clojure.tools.logging.impl/Logger implementation. "clojure.tools.logging.impl/Logger implementation.
The tools.logging API has some significant limits that native Timbre does not. Please note that the tools.logging API has some significant limits
Only use Timbre through tools.logging if you absolutely must (e.g. you're that native Timbre does not. Would strongly recommend against using
Timbre through tools.logging unless you absolutely must (e.g. you're
working with a legacy codebase)." working with a legacy codebase)."
(:require [clojure.tools.logging] (:require [clojure.tools.logging]
[taoensso.encore :as enc]
[taoensso.timbre :as timbre])) [taoensso.timbre :as timbre]))
(deftype Logger [logger-ns] (deftype Logger [logger-ns]
clojure.tools.logging.impl/Logger clojure.tools.logging.impl/Logger
;; Limitations: no support for explicit config, or ns filtering (enabled? [_ level]
(enabled? [_ level] (timbre/log? level)) ;; No support for explicit config, nor ns filtering:
(timbre/log? level))
;; Limitations inline
(write! [_ level throwable message] (write! [_ level throwable message]
(let [config timbre/*config* ; No support for explicit config (timbre/log! level :p
?ns-str nil ; No support [message] ; No support for pre-msg raw args
?file nil ; '' {:config timbre/*config* ; No support for explicit config
?line nil ; '' :?ns-str nil ; Not provided by tools.logging API
msg-type :p ; No support for pre-msg raw args :?file nil ; ''
] :?line nil ; ''
(timbre/log1-fn config level ?ns-str ?file ?line msg-type [message] nil)))) :?err throwable})))
(deftype LoggerFactory [] (deftype LoggerFactory []
clojure.tools.logging.impl/LoggerFactory clojure.tools.logging.impl/LoggerFactory