mirror of https://github.com/status-im/timbre.git
Level checks hk, fix compile-time logging level (prepor)
This commit is contained in:
parent
b21d5c3d6c
commit
e0a9a08b6b
|
@ -43,13 +43,11 @@
|
|||
(comment (stacktrace (Exception. "foo") nil {}))
|
||||
|
||||
;;;; Logging levels
|
||||
;; Level precendence: compile-time > dynamic > atom
|
||||
|
||||
(def level-compile-time
|
||||
"Constant, compile-time logging level determined by the `TIMBRE_LOG_LEVEL`
|
||||
environment variable. When set, overrules dynamically-configurable logging
|
||||
level as a performance optimization (e.g. for use in performance sensitive
|
||||
production environments)."
|
||||
level as a performance optimization."
|
||||
(keyword (System/getenv "TIMBRE_LOG_LEVEL")))
|
||||
|
||||
(def ^:dynamic *level-dynamic* nil)
|
||||
|
@ -63,28 +61,22 @@
|
|||
;;;
|
||||
|
||||
(def levels-ordered [:trace :debug :info :warn :error :fatal :report])
|
||||
(def ^:private levels-scored (assoc (zipmap levels-ordered (next (range))) nil 0))
|
||||
|
||||
(defn error-level? [level] (boolean (#{:error :fatal} level))) ; For appenders, etc.
|
||||
(def levels-scored (zipmap levels-ordered (next (range))))
|
||||
|
||||
(defn- level-error? [level] (boolean (#{:error :fatal} level)))
|
||||
(defn- level-checked-score [level]
|
||||
(or (levels-scored level)
|
||||
(or (when (nil? level) 0) ; < any valid level
|
||||
(levels-scored level)
|
||||
(throw (Exception. (format "Invalid logging level: %s" level)))))
|
||||
|
||||
(def ^:private levels-compare (memoize (fn [x y] (- (level-checked-score x)
|
||||
(level-checked-score y)))))
|
||||
|
||||
(declare config)
|
||||
;; Used in macros, must be public:
|
||||
(defn level-sufficient? [level ; & [config] ; Deprecated
|
||||
]
|
||||
(>= (levels-compare level
|
||||
(or level-compile-time
|
||||
*level-dynamic*
|
||||
;; Deprecate config-specified level:
|
||||
;;(:current-level (or config @config)) ; Don't need compile here
|
||||
(:current-level @config) ; DEPRECATED, here for backwards comp
|
||||
@level-atom)) 0))
|
||||
(defn level-sufficient? "Precendence: compile-time > dynamic > config > atom."
|
||||
[level config] (<= 0 (levels-compare level (or level-compile-time
|
||||
*level-dynamic*
|
||||
(:current-level config)
|
||||
@level-atom))))
|
||||
|
||||
;;;; Default configuration and appenders
|
||||
|
||||
|
@ -136,7 +128,10 @@
|
|||
The `example-config` code contains further settings and details.
|
||||
See also `set-config!`, `merge-config!`, `set-level!`."
|
||||
|
||||
{;;; Control log filtering by namespace patterns (e.g. ["my-app.*"]).
|
||||
{;; Prefer `level-atom` to in-config level when possible:
|
||||
;; :current-logging-level :debug
|
||||
|
||||
;;; Control log filtering by namespace patterns (e.g. ["my-app.*"]).
|
||||
;;; Useful for turning off logging in noisy libraries, etc.
|
||||
:ns-whitelist []
|
||||
:ns-blacklist []
|
||||
|
@ -342,15 +337,14 @@
|
|||
re-pattern (re-find (str ns)) boolean))
|
||||
|
||||
(def compile-config ; Used in macros, must be public
|
||||
"Returns {:appenders-juxt {<level> <wrapped-juxt or nil>}
|
||||
:ns-filter (fn relevant-ns? [ns])}."
|
||||
"Implementation detail.
|
||||
Returns {:appenders-juxt {<level> <wrapped-juxt or nil>}
|
||||
:ns-filter (fn relevant-ns? [ns])}."
|
||||
(memoize
|
||||
;; Careful. The presence of fns actually means that inline config's won't
|
||||
;; actually be identified as samey. In practice not a major (?) problem
|
||||
;; since configs will usually be assigned to a var for which we have proper
|
||||
;; identity.
|
||||
;; Careful. The presence of fns means that inline config's won't correctly
|
||||
;; be identified as samey. In practice not a major (?) problem since configs
|
||||
;; will usually be assigned to a var for which we have proper identity.
|
||||
(fn [{:keys [appenders] :as config}]
|
||||
(assert (map? appenders))
|
||||
{:appenders-juxt
|
||||
(zipmap levels-ordered
|
||||
(->> levels-ordered
|
||||
|
@ -372,26 +366,18 @@
|
|||
(or (empty? ns-blacklist)
|
||||
(not-any? (partial ns-match? ns) ns-blacklist)))))))})))
|
||||
|
||||
(comment (compile-config example-config))
|
||||
(comment (compile-config example-config)
|
||||
(compile-config nil))
|
||||
|
||||
;;;; Logging macros
|
||||
|
||||
(defmacro logging-enabled?
|
||||
"Returns true iff current logging level is sufficient and current namespace
|
||||
unfiltered. The namespace test is runtime, the logging-level test compile-time
|
||||
iff a compile-time logging level was specified."
|
||||
[level & [config]]
|
||||
(if level-compile-time
|
||||
(when (level-sufficient? level)
|
||||
`(let [ns-filter# (:ns-filter (compile-config (or ~config @config)))]
|
||||
(ns-filter# ~(str *ns*))))
|
||||
`(and (level-sufficient? ~level)
|
||||
(let [ns-filter# (:ns-filter (compile-config (or ~config @config)))]
|
||||
(ns-filter# ~(str *ns*))))))
|
||||
(defn ns-unfiltered? [config & [ns]] ((:ns-filter (compile-config config))
|
||||
(or ns *ns*)))
|
||||
|
||||
(comment (def compile-time-level :info)
|
||||
(def compile-time-level nil)
|
||||
(macroexpand-1 '(logging-enabled? :debug)))
|
||||
(defn logging-enabled? "For 3rd-party utils, etc."
|
||||
[level] (let [config' @config]
|
||||
(and (level-sufficient? level config')
|
||||
(ns-unfiltered? config'))))
|
||||
|
||||
(defn send-to-appenders! "Implementation detail."
|
||||
[;; Args provided by both Timbre, tools.logging:
|
||||
|
@ -407,7 +393,7 @@
|
|||
:file file ; No tools.logging support
|
||||
:line line ; No tools.logging support
|
||||
:level level
|
||||
:error? (error-level? level)
|
||||
:error? (level-error? level)
|
||||
:args log-vargs ; No tools.logging support
|
||||
:throwable throwable
|
||||
:message message ; Timbre: nil, tools.logging: nil or string
|
||||
|
@ -420,32 +406,40 @@
|
|||
[base-appender-args msg-type config level & log-args])}
|
||||
[base-appender-args msg-type & [s1 s2 :as sigs]]
|
||||
{:pre [(#{:nil :print-str :format} msg-type)]}
|
||||
`(let [;;; Support [level & log-args], [config level & log-args] sigs:
|
||||
s1# ~s1
|
||||
default-config?# (or (keyword? s1#) (nil? s1#))
|
||||
config# (if default-config?# @config s1#)
|
||||
level# (if default-config?# s1# ~s2)]
|
||||
|
||||
(when (logging-enabled? level# config#)
|
||||
(when-let [juxt-fn# (get-in (compile-config config#)
|
||||
[:appenders-juxt level#])]
|
||||
(let [[x1# & xn# :as xs#] (if default-config?#
|
||||
(vector ~@(next sigs))
|
||||
(vector ~@(nnext sigs)))
|
||||
has-throwable?# (instance? Throwable x1#)
|
||||
log-vargs# (vec (if has-throwable?# xn# xs#))]
|
||||
(send-to-appenders!
|
||||
level#
|
||||
~base-appender-args
|
||||
log-vargs#
|
||||
~(str *ns*)
|
||||
(when has-throwable?# x1#)
|
||||
nil ; Timbre generates msg only after middleware
|
||||
juxt-fn#
|
||||
~msg-type
|
||||
(let [file# ~*file*] (when (not= file# "NO_SOURCE_PATH") file#))
|
||||
;; TODO Waiting on http://dev.clojure.org/jira/browse/CLJ-865:
|
||||
~(:line (meta &form))))))))
|
||||
;; Compile-time:
|
||||
(when (or (nil? level-compile-time)
|
||||
(let [level (cond (levels-scored s1) s1
|
||||
(levels-scored s2) s2)]
|
||||
(or (nil? level) ; Also needs to be compile-time
|
||||
(level-sufficient? level nil))))
|
||||
;; Runtime:
|
||||
`(let [;;; Support [level & log-args], [config level & log-args] sigs:
|
||||
s1# ~s1
|
||||
default-config?# (levels-scored s1#)
|
||||
config# (if default-config?# @config s1#)
|
||||
level# (if default-config?# s1# ~s2)]
|
||||
;; (println "DEBUG: Runtime level check")
|
||||
(when (and (level-sufficient? level# config#)
|
||||
(ns-unfiltered? config#))
|
||||
(when-let [juxt-fn# (get-in (compile-config config#)
|
||||
[:appenders-juxt level#])]
|
||||
(let [[x1# & xn# :as xs#] (if default-config?#
|
||||
(vector ~@(next sigs))
|
||||
(vector ~@(nnext sigs)))
|
||||
has-throwable?# (instance? Throwable x1#)
|
||||
log-vargs# (vec (if has-throwable?# xn# xs#))]
|
||||
(send-to-appenders!
|
||||
level#
|
||||
~base-appender-args
|
||||
log-vargs#
|
||||
~(str *ns*)
|
||||
(when has-throwable?# x1#)
|
||||
nil ; Timbre generates msg only after middleware
|
||||
juxt-fn#
|
||||
~msg-type
|
||||
(let [file# ~*file*] (when (not= file# "NO_SOURCE_PATH") file#))
|
||||
;; TODO Waiting on http://dev.clojure.org/jira/browse/CLJ-865:
|
||||
~(:line (meta &form)))))))))
|
||||
|
||||
(defmacro log
|
||||
"Logs using print-style args. Takes optional logging config (defaults to
|
||||
|
@ -585,4 +579,13 @@
|
|||
{:min-level :error :enabled? true
|
||||
:fmt-output-opts {:nofonts? true}
|
||||
:fn (fn [{:keys [output]}] (str-println output))}}})
|
||||
(log :report (Exception. "Oh noes") "Hello")))
|
||||
(log :report (Exception. "Oh noes") "Hello"))
|
||||
|
||||
;; compile-time level (enabled log* debug println)
|
||||
(def level-compile-time :warn)
|
||||
(debug "hello")
|
||||
|
||||
(log :info "hello") ; Discarded at compile-time
|
||||
(log {} :info) ; Discarded at compile-time
|
||||
(log (or :info) "hello") ; Discarded at runtime
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue