mirror of https://github.com/status-im/timbre.git
Merge branch 'dev'
This commit is contained in:
commit
de5ab76955
|
@ -1,7 +1,7 @@
|
||||||
Current [semantic](http://semver.org/) version:
|
Current [semantic](http://semver.org/) version:
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
[com.taoensso/timbre "1.3.1"]
|
[com.taoensso/timbre "1.4.0"]
|
||||||
```
|
```
|
||||||
|
|
||||||
# Timbre, a (sane) Clojure logging & profiling library
|
# Timbre, a (sane) Clojure logging & profiling library
|
||||||
|
@ -14,7 +14,7 @@ Timbre is an attempt to make **simple logging simple** and more **complex loggin
|
||||||
* Small, uncomplicated **all-Clojure** library.
|
* Small, uncomplicated **all-Clojure** library.
|
||||||
* **Super-simple map-based config**: no arcane XML or properties files!
|
* **Super-simple map-based config**: no arcane XML or properties files!
|
||||||
* **Decent performance** (low overhead).
|
* **Decent performance** (low overhead).
|
||||||
* Flexible **fn-centric appender model**.
|
* Flexible **fn-centric appender model** with **middleware**.
|
||||||
* Sensible built-in appenders including simple **email appender**.
|
* Sensible built-in appenders including simple **email appender**.
|
||||||
* Tunable **flood control** and **asynchronous** logging support.
|
* Tunable **flood control** and **asynchronous** logging support.
|
||||||
* Robust **namespace filtering**.
|
* Robust **namespace filtering**.
|
||||||
|
@ -27,7 +27,7 @@ Timbre is an attempt to make **simple logging simple** and more **complex loggin
|
||||||
Depend on Timbre in your `project.clj`:
|
Depend on Timbre in your `project.clj`:
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
[com.taoensso/timbre "1.3.1"]
|
[com.taoensso/timbre "1.4.0"]
|
||||||
```
|
```
|
||||||
|
|
||||||
and `use` the library:
|
and `use` the library:
|
||||||
|
@ -86,6 +86,8 @@ Configuring Timbre couldn't be simpler. Let's check out (some of) the defaults:
|
||||||
:ns-whitelist []
|
:ns-whitelist []
|
||||||
:ns-blacklist []
|
:ns-blacklist []
|
||||||
|
|
||||||
|
:middleware [] ; As of 1.4.0, see source code
|
||||||
|
|
||||||
:timestamp-pattern "yyyy-MMM-dd HH:mm:ss ZZ"
|
:timestamp-pattern "yyyy-MMM-dd HH:mm:ss ZZ"
|
||||||
:timestamp-locale nil
|
:timestamp-locale nil
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
(defproject com.taoensso/timbre "1.3.1"
|
(defproject com.taoensso/timbre "1.4.0"
|
||||||
:description "Clojure logging & profiling library"
|
:description "Clojure logging & profiling library"
|
||||||
:url "https://github.com/ptaoussanis/timbre"
|
:url "https://github.com/ptaoussanis/timbre"
|
||||||
:license {:name "Eclipse Public License"}
|
:license {:name "Eclipse Public License"}
|
||||||
|
|
|
@ -41,23 +41,26 @@
|
||||||
;;;; Default configuration and appenders
|
;;;; Default configuration and appenders
|
||||||
|
|
||||||
(utils/defonce* config
|
(utils/defonce* config
|
||||||
"This map atom controls everything about the way Timbre operates. In
|
"This map atom controls everything about the way Timbre operates.
|
||||||
particular note the flexibility to add arbitrary appenders.
|
|
||||||
|
|
||||||
An appender is a map with keys:
|
APPENDERS
|
||||||
:doc, :min-level, :enabled?, :async?, :max-message-per-msecs, :fn?
|
An appender is a map with keys:
|
||||||
|
:doc, :min-level, :enabled?, :async?, :max-message-per-msecs, :fn
|
||||||
|
|
||||||
An appender's fn takes a single map argument with keys:
|
An appender's fn takes a single map argument with keys:
|
||||||
:level, :message, :more ; From all logging macros (`info`, etc.)
|
:level, :message, :more ; From all logging macros (`info`, etc.)
|
||||||
:profiling-stats ; From `profile` macro
|
:profiling-stats ; From `profile` macro
|
||||||
:ap-config ; `shared-appender-config`
|
:ap-config ; `shared-appender-config`
|
||||||
:prefix ; Output of `prefix-fn`
|
:prefix ; Output of `prefix-fn`
|
||||||
|
And also: :instant, :timestamp, :hostname, :ns, :error?
|
||||||
|
|
||||||
Other keys include: :instant, :timestamp, :hostname, :ns, :error?
|
MIDDLEWARE
|
||||||
|
Middleware are fns (applied right-to-left) that transform the map argument
|
||||||
|
dispatched to appender fns. If any middleware returns nil, no dispatching
|
||||||
|
will occur (i.e. the event will be filtered).
|
||||||
|
|
||||||
See source code for examples.
|
See source code for examples. See `set-config!`, `merge-config!`, `set-level!`
|
||||||
See `set-config!`, `merge-config!`, `set-level!` for convenient config
|
for convenient config editing."
|
||||||
editing."
|
|
||||||
(atom {:current-level :debug
|
(atom {:current-level :debug
|
||||||
|
|
||||||
;;; Control log filtering by namespace patterns (e.g. ["my-app.*"]).
|
;;; Control log filtering by namespace patterns (e.g. ["my-app.*"]).
|
||||||
|
@ -65,16 +68,9 @@
|
||||||
:ns-whitelist []
|
:ns-whitelist []
|
||||||
:ns-blacklist []
|
:ns-blacklist []
|
||||||
|
|
||||||
;; TODO Generalized transformation/filtering unary fns to operate on
|
;; Fns (applied right-to-left) to transform/filter appender fn args.
|
||||||
;; logging requests to either either filter or transform logging
|
;; Useful for obfuscating credentials, pattern filtering, etc.
|
||||||
;; messages (e.g. obscure security credentials).
|
:middleware []
|
||||||
;;
|
|
||||||
;; Could use a cacheable comp/juxt and include ns white/black list
|
|
||||||
;; functionality? Possibly even just prepend to the regular appender
|
|
||||||
;; juxt (assuming we keep ns filtering separate)? Note that this'd
|
|
||||||
;; also make any additional middlware cost async-able.
|
|
||||||
;;
|
|
||||||
;; :middleware []
|
|
||||||
|
|
||||||
;;; Control :timestamp format
|
;;; Control :timestamp format
|
||||||
:timestamp-pattern "yyyy-MMM-dd HH:mm:ss ZZ" ; SimpleDateFormat pattern
|
:timestamp-pattern "yyyy-MMM-dd HH:mm:ss ZZ" ; SimpleDateFormat pattern
|
||||||
|
@ -133,44 +129,12 @@
|
||||||
|
|
||||||
;;;; Appender-fn decoration
|
;;;; Appender-fn decoration
|
||||||
|
|
||||||
(defn- make-timestamp-fn
|
|
||||||
"Returns a unary fn that formats instants using given pattern string and an
|
|
||||||
optional Locale."
|
|
||||||
[^String pattern ^Locale locale]
|
|
||||||
(let [format (if locale
|
|
||||||
(SimpleDateFormat. pattern locale)
|
|
||||||
(SimpleDateFormat. pattern))]
|
|
||||||
(fn [^Date instant] (.format ^SimpleDateFormat format instant))))
|
|
||||||
|
|
||||||
(comment ((make-timestamp-fn "yyyy-MMM-dd" nil) (Date.)))
|
|
||||||
|
|
||||||
(def get-hostname
|
|
||||||
(utils/memoize-ttl
|
|
||||||
60000 (fn [] (.. java.net.InetAddress getLocalHost getHostName))))
|
|
||||||
|
|
||||||
(defn- wrap-appender-fn
|
(defn- wrap-appender-fn
|
||||||
"Wraps compile-time appender fn with additional runtime capabilities
|
"Wraps compile-time appender fn with additional runtime capabilities
|
||||||
controlled by compile-time config."
|
controlled by compile-time config."
|
||||||
[{apfn :fn :keys [async? max-message-per-msecs] :as appender}]
|
[{apfn :fn :keys [async? max-message-per-msecs] :as appender}]
|
||||||
(->
|
(->> ; Wrapping applies capabilities bottom-to-top
|
||||||
;; Wrap to add compile-time stuff to runtime appender arguments
|
apfn
|
||||||
(let [{ap-config :shared-appender-config
|
|
||||||
:keys [timestamp-pattern timestamp-locale prefix-fn]} @config
|
|
||||||
|
|
||||||
timestamp-fn (make-timestamp-fn timestamp-pattern timestamp-locale)]
|
|
||||||
|
|
||||||
(fn [{:keys [instant] :as apfn-args}]
|
|
||||||
(let [apfn-args (merge apfn-args {:ap-config ap-config
|
|
||||||
:timestamp (timestamp-fn instant)
|
|
||||||
:hostname (get-hostname)})]
|
|
||||||
(apfn (assoc apfn-args :prefix (prefix-fn apfn-args))))))
|
|
||||||
|
|
||||||
;; Wrap for asynchronicity support
|
|
||||||
((fn [apfn]
|
|
||||||
(if-not async?
|
|
||||||
apfn
|
|
||||||
(let [agent (agent nil :error-mode :continue)]
|
|
||||||
(fn [apfn-args] (send-off agent (fn [_] (apfn apfn-args))))))))
|
|
||||||
|
|
||||||
;; Wrap for runtime flood-safety support
|
;; Wrap for runtime flood-safety support
|
||||||
((fn [apfn]
|
((fn [apfn]
|
||||||
|
@ -199,7 +163,60 @@
|
||||||
(->> (keys timers-snapshot)
|
(->> (keys timers-snapshot)
|
||||||
(filter #(allow? (timers-snapshot %))))]
|
(filter #(allow? (timers-snapshot %))))]
|
||||||
(when (seq expired-timers)
|
(when (seq expired-timers)
|
||||||
(apply swap! flood-timers dissoc expired-timers))))))))))))
|
(apply swap! flood-timers dissoc expired-timers))))))))))
|
||||||
|
|
||||||
|
;; Wrap for async (agent) support
|
||||||
|
((fn [apfn]
|
||||||
|
(if-not async?
|
||||||
|
apfn
|
||||||
|
(let [agent (agent nil :error-mode :continue)]
|
||||||
|
(fn [apfn-args] (send-off agent (fn [_] (apfn apfn-args))))))))))
|
||||||
|
|
||||||
|
(defn- make-timestamp-fn
|
||||||
|
"Returns a unary fn that formats instants using given pattern string and an
|
||||||
|
optional Locale."
|
||||||
|
[^String pattern ^Locale locale]
|
||||||
|
(let [format (if locale
|
||||||
|
(SimpleDateFormat. pattern locale)
|
||||||
|
(SimpleDateFormat. pattern))]
|
||||||
|
(fn [^Date instant] (.format ^SimpleDateFormat format instant))))
|
||||||
|
|
||||||
|
(comment ((make-timestamp-fn "yyyy-MMM-dd" nil) (Date.)))
|
||||||
|
|
||||||
|
(def get-hostname
|
||||||
|
(utils/memoize-ttl
|
||||||
|
60000 (fn [] (.. java.net.InetAddress getLocalHost getHostName))))
|
||||||
|
|
||||||
|
(defn- wrap-appender-juxt
|
||||||
|
"Wraps compile-time appender juxt with additional runtime capabilities
|
||||||
|
(incl. middleware) controller by compile-time config. Like `wrap-appender-fn`
|
||||||
|
but operates on the entire juxt at once."
|
||||||
|
[juxtfn]
|
||||||
|
(->> ; Wrapping applies capabilities bottom-to-top
|
||||||
|
juxtfn
|
||||||
|
|
||||||
|
;; Wrap to add middleware transforms/filters
|
||||||
|
((fn [juxtfn]
|
||||||
|
(if-let [middleware (seq (:middleware @config))]
|
||||||
|
(let [composed-middleware
|
||||||
|
(apply comp (map (fn [mf] (fn [args] (when args (mf args))))
|
||||||
|
middleware))]
|
||||||
|
(fn [juxtfn-args]
|
||||||
|
(when-let [juxtfn-args (composed-middleware juxtfn-args)]
|
||||||
|
(juxtfn juxtfn-args))))
|
||||||
|
juxtfn)))
|
||||||
|
|
||||||
|
;; Wrap to add compile-time stuff to runtime appender arguments
|
||||||
|
((fn [juxtfn]
|
||||||
|
(let [{ap-config :shared-appender-config
|
||||||
|
:keys [timestamp-pattern timestamp-locale prefix-fn]} @config
|
||||||
|
|
||||||
|
timestamp-fn (make-timestamp-fn timestamp-pattern timestamp-locale)]
|
||||||
|
(fn [{:keys [instant] :as juxtfn-args}]
|
||||||
|
(let [juxtfn-args (merge juxtfn-args {:ap-config ap-config
|
||||||
|
:timestamp (timestamp-fn instant)
|
||||||
|
:hostname (get-hostname)})]
|
||||||
|
(juxtfn (assoc juxtfn-args :prefix (prefix-fn juxtfn-args))))))))))
|
||||||
|
|
||||||
;;;; Caching
|
;;;; Caching
|
||||||
|
|
||||||
|
@ -208,7 +225,7 @@
|
||||||
(def appenders-juxt-cache
|
(def appenders-juxt-cache
|
||||||
"Per-level, combined relevant appender-fns to allow for fast runtime
|
"Per-level, combined relevant appender-fns to allow for fast runtime
|
||||||
appender-fn dispatch:
|
appender-fn dispatch:
|
||||||
{:level (juxt wrapped-appender-fn wrapped-appender-fn ...) or nil
|
{:level (wrapped-juxt wrapped-appender-fn wrapped-appender-fn ...) or nil
|
||||||
...}"
|
...}"
|
||||||
(atom {}))
|
(atom {}))
|
||||||
|
|
||||||
|
@ -233,10 +250,11 @@
|
||||||
(when-let [ap-ids (keys rel-aps)]
|
(when-let [ap-ids (keys rel-aps)]
|
||||||
(->> ap-ids
|
(->> ap-ids
|
||||||
(map #(wrap-appender-fn (rel-aps %)))
|
(map #(wrap-appender-fn (rel-aps %)))
|
||||||
(apply juxt))))))))
|
(apply juxt)
|
||||||
|
(wrap-appender-juxt))))))))
|
||||||
(reset! appenders-juxt-cache)))
|
(reset! appenders-juxt-cache)))
|
||||||
|
|
||||||
;;; Namespace filter ; TODO Generalize to arbitrary configurable middleware juxt?
|
;;; Namespace filter
|
||||||
|
|
||||||
(def ns-filter-cache "@ns-filter-cache => (fn relevant-ns? [ns] ...)"
|
(def ns-filter-cache "@ns-filter-cache => (fn relevant-ns? [ns] ...)"
|
||||||
(atom (constantly true)))
|
(atom (constantly true)))
|
||||||
|
@ -372,4 +390,16 @@
|
||||||
(spy (* 6 5 4 3 2 1))
|
(spy (* 6 5 4 3 2 1))
|
||||||
(spy :debug :factorial6 (* 6 5 4 3 2 1))
|
(spy :debug :factorial6 (* 6 5 4 3 2 1))
|
||||||
(info (Exception. "noes!") "bar")
|
(info (Exception. "noes!") "bar")
|
||||||
(spy (/ 4 0)))
|
(spy (/ 4 0))
|
||||||
|
|
||||||
|
;; Middleware
|
||||||
|
(info {:name "Robert Paulson" :password "Super secret"})
|
||||||
|
(set-config!
|
||||||
|
[:middleware]
|
||||||
|
[(fn [{:keys [hostname message] :as args}]
|
||||||
|
(cond (= hostname "filtered-host") nil ; Filter
|
||||||
|
(map? message)
|
||||||
|
(if (contains? message :password)
|
||||||
|
(assoc args :message (assoc message :password "*****"))
|
||||||
|
args)
|
||||||
|
:else args))]))
|
Loading…
Reference in New Issue