mirror of https://github.com/status-im/timbre.git
Housekeeping
This commit is contained in:
parent
3c824d31da
commit
beec0c9fdd
|
@ -14,7 +14,6 @@
|
||||||
;;;; TODO
|
;;;; TODO
|
||||||
;; - Check for successful cljs compile
|
;; - Check for successful cljs compile
|
||||||
;; - Cljs default appenders
|
;; - Cljs default appenders
|
||||||
;; - Port appenders
|
|
||||||
;; - Try ease backward comp, update README, CHANGELOG
|
;; - Try ease backward comp, update README, CHANGELOG
|
||||||
;; - Document shutdown-agents,
|
;; - Document shutdown-agents,
|
||||||
;; Ref. https://github.com/ptaoussanis/timbre/pull/100/files
|
;; Ref. https://github.com/ptaoussanis/timbre/pull/100/files
|
||||||
|
@ -36,13 +35,17 @@
|
||||||
|
|
||||||
#+clj
|
#+clj
|
||||||
(def default-timestamp-opts
|
(def default-timestamp-opts
|
||||||
|
"Controls (:timestamp_ data)."
|
||||||
{:pattern "yy-MMM-dd HH:mm:ss"
|
{:pattern "yy-MMM-dd HH:mm:ss"
|
||||||
:locale (java.util.Locale. "en")
|
:locale (java.util.Locale. "en")
|
||||||
;; :timezone (java.util.TimeZone/getTimeZone "UTC")
|
;; :timezone (java.util.TimeZone/getTimeZone "UTC")
|
||||||
:timezone (java.util.TimeZone/getDefault)})
|
:timezone (java.util.TimeZone/getDefault)})
|
||||||
|
|
||||||
(declare stacktrace)
|
(declare stacktrace)
|
||||||
(defn default-output-fn [data & [opts]]
|
|
||||||
|
(defn default-output-fn
|
||||||
|
"(fn [data & [opts]]) -> string output."
|
||||||
|
[data & [opts]]
|
||||||
(let [{:keys [level ?err_ vargs_ msg_ ?ns-str hostname_ timestamp_]} data]
|
(let [{:keys [level ?err_ vargs_ msg_ ?ns-str hostname_ timestamp_]} data]
|
||||||
(str
|
(str
|
||||||
#+clj (force timestamp_) #+clj " "
|
#+clj (force timestamp_) #+clj " "
|
||||||
|
@ -54,33 +57,37 @@
|
||||||
(declare default-err default-out ensure-spit-dir-exists!)
|
(declare default-err default-out ensure-spit-dir-exists!)
|
||||||
|
|
||||||
(def example-config
|
(def example-config
|
||||||
"Example (+default) Timbre config map.
|
"Example (+default) Timbre v4 config map.
|
||||||
|
|
||||||
APPENDERS
|
APPENDERS
|
||||||
|
|
||||||
|
*** Please see the `taoensso.timbre.appenders.example-appender` ns if you
|
||||||
|
plan to write your own Timbre appender ***
|
||||||
|
|
||||||
An appender is a map with keys:
|
An appender is a map with keys:
|
||||||
:doc ; Optional docstring.
|
:doc ; Optional docstring
|
||||||
:min-level ; Level keyword, or nil (=> no minimum level).
|
:min-level ; Level keyword, or nil (=> no minimum level)
|
||||||
:enabled? ;
|
:enabled? ;
|
||||||
:async? ; Dispatch using agent? Useful for slow appenders.
|
:async? ; Dispatch using agent? Useful for slow appenders
|
||||||
:rate-limit ; [[ncalls-limit window-ms] <...>], or nil.
|
:rate-limit ; [[ncalls-limit window-ms] <...>], or nil
|
||||||
:data-hash-fn ; Used by rate-limiter, etc.
|
:data-hash-fn ; Used by rate-limiter, etc.
|
||||||
:opts ; Any appender-specific opts
|
:opts ; Any appender-specific opts
|
||||||
:fn ; (fn [data-map]), with keys described below.
|
:fn ; (fn [data-map]), with keys described below
|
||||||
|
|
||||||
An appender's fn takes a single data map with keys:
|
An appender's fn takes a single data map with keys:
|
||||||
:config ; Entire config map (this map)
|
:config ; Entire config map (this map, etc.)
|
||||||
:appender-id ; Id of appender currently being dispatched to
|
:appender-id ; Id of appender currently being dispatched to
|
||||||
:appender ; Entire appender map currently being dispatched to
|
:appender ; Entire appender map currently being dispatched to
|
||||||
:appender-opts ; (:opts (:appender <data-map>)), for convenience
|
:appender-opts ; Duplicates (:opts <appender-map>), for convenience
|
||||||
|
|
||||||
:instant ; java.util.Date or js/Date
|
:instant ; Platform date (java.util.Date or js/Date)
|
||||||
:level ; Keyword
|
:level ; Keyword
|
||||||
:error-level? ; Is level :error or :fatal?
|
:error-level? ; Is level :error or :fatal?
|
||||||
:?ns-str ; String, or nil
|
:?ns-str ; String, or nil
|
||||||
:?file ; String, or nil (waiting on CLJ-865)
|
:?file ; String, or nil ; Waiting on CLJ-865
|
||||||
:?line ; Integer, or nil ('')
|
:?line ; Integer, or nil ; Waiting on CLJ-865
|
||||||
|
|
||||||
:?err_ ; Delay - first-argument error
|
:?err_ ; Delay - first-argument platform error
|
||||||
:vargs_ ; Delay - raw args vector
|
:vargs_ ; Delay - raw args vector
|
||||||
:hostname_ ; Delay - string (clj only)
|
:hostname_ ; Delay - string (clj only)
|
||||||
:msg_ ; Delay - args string
|
:msg_ ; Delay - args string
|
||||||
|
@ -91,45 +98,46 @@
|
||||||
|
|
||||||
<Also, any *context* keys, which get merged into data map>
|
<Also, any *context* keys, which get merged into data map>
|
||||||
|
|
||||||
DELAYS
|
|
||||||
As a matter of middleware hygiene, prefer using `force` to @/`deref`
|
|
||||||
when retrieving getting delayed values.
|
|
||||||
|
|
||||||
MIDDLEWARE
|
MIDDLEWARE
|
||||||
Middleware are fns (applied left->right) that transform the data map
|
Middleware are simple (fn [data]) -> ?data fns (applied left->right) that
|
||||||
dispatched to appender fns. If any middleware returns nil, NO dispatching
|
transform the data map dispatched to appender fns. If any middleware returns
|
||||||
will occur (i.e. the event will be filtered).
|
nil, NO dispatching will occur (i.e. the event will be filtered).
|
||||||
|
|
||||||
The `example-config` source code contains further settings and details.
|
The `example-config` source code contains further settings and details.
|
||||||
See also `set-config!`, `merge-config!`, `set-level!`."
|
See also `set-config!`, `merge-config!`, `set-level!`."
|
||||||
|
|
||||||
(merge
|
(merge
|
||||||
{:level :debug
|
{:level :debug ; e/o #{:trace :debug :info :warn :error :fatal :report}
|
||||||
|
|
||||||
:whitelist [] ; "my-ns.*", etc.
|
:whitelist [] ; "my-ns.*", etc.
|
||||||
:blacklist [] ;
|
:blacklist [] ;
|
||||||
:middleware [] ; (fns [data]) -> ?data, applied left->right
|
:middleware [] ; (fns [data]) -> ?data, applied left->right
|
||||||
|
|
||||||
:output-fn default-output-fn
|
:output-fn default-output-fn ; (fn [data]) -> string
|
||||||
#+clj :timestamp-opts #+clj default-timestamp-opts
|
#+clj :timestamp-opts
|
||||||
|
#+clj default-timestamp-opts ; {:pattern _ :locale _ :timezone _}
|
||||||
|
|
||||||
:appenders
|
:appenders
|
||||||
#+clj
|
#+clj
|
||||||
{:println
|
{:println ; Appender id
|
||||||
|
;; Appender <map>:
|
||||||
{:doc "Prints to (:stream <appender-opts>) IO stream. Enabled by default."
|
{:doc "Prints to (:stream <appender-opts>) IO stream. Enabled by default."
|
||||||
:min-level nil :enabled? true :async? false :rate-limit nil
|
:min-level nil :enabled? true :async? false :rate-limit nil
|
||||||
:opts {;; e/o #{:std-err :std-out :auto <stream>}:
|
|
||||||
:stream :auto}
|
;; Any custom appender opts:
|
||||||
|
:opts {:stream :auto ; e/o #{:std-err :std-out :auto <stream>}
|
||||||
|
}
|
||||||
|
|
||||||
:fn
|
:fn
|
||||||
(fn [data]
|
(fn [data]
|
||||||
(let [{:keys [output-fn error? appender-opts]} data
|
(let [{:keys [output-fn error? appender-opts]} data
|
||||||
{:keys [stream]} appender-opts
|
{:keys [stream]} appender-opts
|
||||||
out (case stream
|
stream (case stream
|
||||||
(nil :auto) (if error? default-err *out*)
|
(nil :auto) (if error? default-err *out*)
|
||||||
:std-err default-err
|
:std-err default-err
|
||||||
:std-out default-out
|
:std-out default-out
|
||||||
stream)]
|
stream)]
|
||||||
(binding [*out* out] (println (output-fn data)))))}
|
(binding [*out* stream] (println (output-fn data)))))}
|
||||||
|
|
||||||
:spit
|
:spit
|
||||||
{:doc "Spits to (:spit-filename <appender-opts>) file."
|
{:doc "Spits to (:spit-filename <appender-opts>) file."
|
||||||
|
@ -148,7 +156,7 @@
|
||||||
(set-config! example-config)
|
(set-config! example-config)
|
||||||
(infof "Hello %s" "world :-)"))
|
(infof "Hello %s" "world :-)"))
|
||||||
|
|
||||||
(enc/defonce* ^:dynamic *config* example-config)
|
(enc/defonce* ^:dynamic *config* "See `example-config` for info." example-config)
|
||||||
(defmacro with-config [config & body] `(binding [*config* ~config] ~@body))
|
(defmacro with-config [config & body] `(binding [*config* ~config] ~@body))
|
||||||
(defmacro with-merged-config [config & body]
|
(defmacro with-merged-config [config & body]
|
||||||
`(binding [*config* (enc/nested-merge *config* ~config)] ~@body))
|
`(binding [*config* (enc/nested-merge *config* ~config)] ~@body))
|
||||||
|
@ -157,7 +165,7 @@
|
||||||
#+cljs (set! *config* (f *config*))
|
#+cljs (set! *config* (f *config*))
|
||||||
#+clj (alter-var-root #'*config* f))
|
#+clj (alter-var-root #'*config* f))
|
||||||
|
|
||||||
(defn set-config! [m] (swap-config! (fn [_] m)))
|
(defn set-config! [m] (swap-config! (fn [_old] m)))
|
||||||
(defn merge-config! [m] (swap-config! (fn [old] (enc/nested-merge old m))))
|
(defn merge-config! [m] (swap-config! (fn [old] (enc/nested-merge old m))))
|
||||||
|
|
||||||
(defn set-level! [level] (swap-config! (fn [m] (merge m {:level level}))))
|
(defn set-level! [level] (swap-config! (fn [m] (merge m {:level level}))))
|
||||||
|
@ -196,6 +204,7 @@
|
||||||
;;;; ns filter
|
;;;; ns filter
|
||||||
|
|
||||||
(def ^:private compile-ns-filters
|
(def ^:private compile-ns-filters
|
||||||
|
"(fn [whitelist blacklist]) -> (fn [ns]) -> ?unfiltered-ns"
|
||||||
(let [->re-pattern
|
(let [->re-pattern
|
||||||
(fn [x]
|
(fn [x]
|
||||||
(enc/cond!
|
(enc/cond!
|
||||||
|
@ -213,7 +222,7 @@
|
||||||
|
|
||||||
white-filter
|
white-filter
|
||||||
(cond
|
(cond
|
||||||
;; (nil? whitelist) (fn [ns] false)
|
;; (nil? whitelist) (fn [ns] false) ; Might be surprising
|
||||||
(empty? whitelist*) (fn [ns] true)
|
(empty? whitelist*) (fn [ns] true)
|
||||||
:else (fn [ns] (some #(re-find % ns) whitelist*)))
|
:else (fn [ns] (some #(re-find % ns) whitelist*)))
|
||||||
|
|
||||||
|
@ -222,13 +231,13 @@
|
||||||
(empty? blacklist*) (fn [ns] true)
|
(empty? blacklist*) (fn [ns] true)
|
||||||
:else (fn [ns] (not (some #(re-find % ns) blacklist*))))]
|
:else (fn [ns] (not (some #(re-find % ns) blacklist*))))]
|
||||||
|
|
||||||
[white-filter black-filter])))))
|
(fn [ns] (when (and (white-filter ns) (black-filter ns)) ns)))))))
|
||||||
|
|
||||||
(def ^:private ns-filter
|
(def ^:private ns-filter
|
||||||
|
"(fn [whitelist blacklist ns]) -> ?unfiltered-ns"
|
||||||
(enc/memoize_
|
(enc/memoize_
|
||||||
(fn [whitelist blacklist ns]
|
(fn [whitelist blacklist ns]
|
||||||
(let [[white-filter black-filter] (compile-ns-filters whitelist blacklist)]
|
((compile-ns-filters whitelist blacklist) ns))))
|
||||||
(when (and (white-filter ns) (black-filter ns)) ns)))))
|
|
||||||
|
|
||||||
(comment (qb 10000 (ns-filter ["foo.*"] ["foo.baz"] "foo.bar")))
|
(comment (qb 10000 (ns-filter ["foo.*"] ["foo.baz"] "foo.bar")))
|
||||||
|
|
||||||
|
@ -240,17 +249,17 @@
|
||||||
|
|
||||||
;;;; Utils
|
;;;; Utils
|
||||||
|
|
||||||
(defmacro delay-vec [coll] (mapv (fn [in] `(delay ~in)) coll))
|
(defn- ->delay [x] (if (delay? x) x (delay x)))
|
||||||
(comment
|
|
||||||
(qb 10000 (delay :foo) (fn [] :foo))
|
|
||||||
(macroexpand '(delay-vec [(do (println "hi") :x) :y :z])))
|
|
||||||
|
|
||||||
(defn- vsplit-err1 [[v1 :as v]] (if-not (enc/error? v1) [nil v] (enc/vsplit-first v)))
|
(defn- vsplit-err1 [[v1 :as v]] (if-not (enc/error? v1) [nil v] (enc/vsplit-first v)))
|
||||||
(comment
|
(comment
|
||||||
(vsplit-err1 [:a :b :c])
|
(vsplit-err1 [:a :b :c])
|
||||||
(vsplit-err1 [(Exception.) :a :b :c]))
|
(vsplit-err1 [(Exception.) :a :b :c]))
|
||||||
|
|
||||||
(defn default-data-hash-fn [data]
|
(defn default-data-hash-fn
|
||||||
|
"Used for rate limiters, some appenders (e.g. Carmine), etc.
|
||||||
|
Goal: (hash data-1) = (hash data-2) iff data-1 \"the same\" as data-2 for
|
||||||
|
rate-limiting purposes, etc."
|
||||||
|
[data]
|
||||||
(let [{:keys [?ns-str ?line vargs_]} data
|
(let [{:keys [?ns-str ?line vargs_]} data
|
||||||
vargs (force vargs_)]
|
vargs (force vargs_)]
|
||||||
(str
|
(str
|
||||||
|
@ -272,7 +281,11 @@
|
||||||
|
|
||||||
;;;; Logging core
|
;;;; Logging core
|
||||||
|
|
||||||
(defn log? [level & [?ns-str config]]
|
(defn log?
|
||||||
|
"Would Timbre currently log at the given logging level?
|
||||||
|
* ns filtering requires a compile-time `?ns-str` to be provided.
|
||||||
|
* Non-global config requires an explicit `config` to be provided."
|
||||||
|
[level & [?ns-str config]]
|
||||||
(let [config (or config *config*)]
|
(let [config (or config *config*)]
|
||||||
(and (level>= level (get-active-level config))
|
(and (level>= level (get-active-level config))
|
||||||
(ns-filter (:whitelist config) (:blacklist config) (or ?ns-str ""))
|
(ns-filter (:whitelist config) (:blacklist config) (or ?ns-str ""))
|
||||||
|
@ -280,16 +293,18 @@
|
||||||
|
|
||||||
(comment (log? :trace))
|
(comment (log? :trace))
|
||||||
|
|
||||||
(def ^:dynamic *context* "General-purpose dynamic logging context." nil)
|
(def ^:dynamic *context*
|
||||||
|
"General-purpose dynamic logging context. Context will be merged into
|
||||||
|
appender 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)
|
||||||
|
|
||||||
(defn log* "Core fn-level logger. Implementation detail."
|
(defn log* "Core fn-level logger. Implementation detail."
|
||||||
[config level ?ns-str ?file ?line msg-type dvargs & [base-data]]
|
[config level ?ns-str ?file ?line msg-type vargs_ & [base-data]]
|
||||||
(when (log? level ?ns-str config)
|
(when (log? level ?ns-str config)
|
||||||
(let [instant (enc/now-dt)
|
(let [instant (enc/now-dt)
|
||||||
vargs*_ (delay (vsplit-err1 (mapv force dvargs)))
|
vargs*_ (delay (vsplit-err1 (force vargs_)))
|
||||||
?err_ (delay (get @vargs*_ 0))
|
?err_ (delay (get @vargs*_ 0))
|
||||||
vargs_ (delay (get @vargs*_ 1))
|
vargs_ (delay (get @vargs*_ 1))
|
||||||
data (merge base-data *context*
|
data (merge base-data *context*
|
||||||
|
@ -305,7 +320,7 @@
|
||||||
#+clj :hostname_ #+clj (delay (get-hostname))
|
#+clj :hostname_ #+clj (delay (get-hostname))
|
||||||
:error-level? (#{:error :fatal} level)})
|
:error-level? (#{:error :fatal} level)})
|
||||||
msg-fn
|
msg-fn
|
||||||
(fn [vargs_] ; *After* middleware, etc.
|
(fn [vargs_] ; For use *after* middleware, etc.
|
||||||
(when-not (nil? msg-type)
|
(when-not (nil? msg-type)
|
||||||
(when-let [vargs (have [:or nil? vector?] (force vargs_))]
|
(when-let [vargs (have [:or nil? vector?] (force vargs_))]
|
||||||
(case msg-type
|
(case msg-type
|
||||||
|
@ -340,7 +355,7 @@
|
||||||
(not (rl-fn data-hash))))))
|
(not (rl-fn data-hash))))))
|
||||||
|
|
||||||
(let [{:keys [async?] apfn :fn} appender
|
(let [{:keys [async?] apfn :fn} appender
|
||||||
msg_ (delay (msg-fn (:vargs_ data)))
|
msg_ (delay (or (msg-fn (:vargs_ data)) #_""))
|
||||||
output-fn (or (:output-fn appender)
|
output-fn (or (:output-fn appender)
|
||||||
(:output-fn config)
|
(:output-fn config)
|
||||||
default-output-fn)
|
default-output-fn)
|
||||||
|
@ -356,26 +371,25 @@
|
||||||
{:locale locale :timezone timezone})
|
{:locale locale :timezone timezone})
|
||||||
(:instant data))))
|
(:instant data))))
|
||||||
|
|
||||||
data
|
data ; Final data prep before going to appender
|
||||||
(merge data
|
(merge data
|
||||||
{:appender-id id
|
{:appender-id id
|
||||||
:appender appender
|
:appender appender
|
||||||
:appender-opts (:opts appender)
|
:appender-opts (:opts appender) ; For convenience
|
||||||
:msg_ msg_
|
:msg_ msg_
|
||||||
:msg-fn msg-fn
|
:msg-fn msg-fn
|
||||||
:output-fn output-fn
|
:output-fn output-fn
|
||||||
#+clj :timestamp_ #+clj timestamp_})]
|
#+clj :timestamp_ #+clj timestamp_})]
|
||||||
|
|
||||||
(if-not async?
|
(if-not async?
|
||||||
(apfn data)
|
(apfn data) ; Allow errors to throw
|
||||||
(send-off (get-agent id) (fn [_] (apfn data)))))))
|
(send-off (get-agent id) (fn [_] (apfn data)))))))
|
||||||
nil
|
nil
|
||||||
(enc/clj1098 (:appenders config))))))
|
(enc/clj1098 (:appenders config))))))
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(log* *config* :info
|
(log* *config* :info nil nil nil :print (delay [(do (println "hi") :x) :y])))
|
||||||
nil nil nil :print (delay-vec [(do (println "hi") :x) :y])))
|
|
||||||
|
|
||||||
;;;; Logging macros
|
;;;; Logging macros
|
||||||
|
|
||||||
|
@ -391,7 +405,7 @@
|
||||||
;; TODO Waiting on http://dev.clojure.org/jira/browse/CLJ-865:
|
;; TODO Waiting on http://dev.clojure.org/jira/browse/CLJ-865:
|
||||||
?line (:line (meta &form))]
|
?line (:line (meta &form))]
|
||||||
`(log* ~config ~level ~ns-str ~?file ~?line ~msg-type
|
`(log* ~config ~level ~ns-str ~?file ~?line ~msg-type
|
||||||
(delay-vec ~args) ~base-data)))))
|
(delay ~(vec args)) ~base-data)))))
|
||||||
|
|
||||||
(defmacro ^:private def-logger [level]
|
(defmacro ^:private def-logger [level]
|
||||||
(let [level-name (name level)]
|
(let [level-name (name level)]
|
||||||
|
@ -462,7 +476,7 @@
|
||||||
(require '[taoensso.timbre.profiling :as profiling
|
(require '[taoensso.timbre.profiling :as profiling
|
||||||
:refer (pspy pspy* profile defnp p p*)]))
|
:refer (pspy pspy* profile defnp p p*)]))
|
||||||
|
|
||||||
;;;; Public utils
|
;;;; Misc public utils
|
||||||
|
|
||||||
#+clj
|
#+clj
|
||||||
(defn color-str [color & xs]
|
(defn color-str [color & xs]
|
||||||
|
@ -475,7 +489,6 @@
|
||||||
|
|
||||||
#+clj (def default-out (java.io.OutputStreamWriter. System/out))
|
#+clj (def default-out (java.io.OutputStreamWriter. System/out))
|
||||||
#+clj (def default-err (java.io.PrintWriter. System/err))
|
#+clj (def default-err (java.io.PrintWriter. System/err))
|
||||||
|
|
||||||
(defmacro with-default-outs [& body]
|
(defmacro with-default-outs [& body]
|
||||||
`(binding [*out* default-out, *err* default-err] ~@body))
|
`(binding [*out* default-out, *err* default-err] ~@body))
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
(ns taoensso.timbre.appenders.example-appender
|
||||||
|
"An example of how Timbre appenders should be written.
|
||||||
|
Please mention any requirements/dependencies in this docstring."
|
||||||
|
{:author "Peter Taoussanis"} ; <- Your name here
|
||||||
|
(:require [clojure.string :as str]
|
||||||
|
[taoensso.timbre :as timbre]
|
||||||
|
[taoensso.encore :as encore]))
|
||||||
|
|
||||||
|
;;;; Any private util fns, etc.
|
||||||
|
|
||||||
|
;; ...
|
||||||
|
|
||||||
|
;;;;
|
||||||
|
|
||||||
|
(defn make-appender-fn
|
||||||
|
"(fn [make-config-map]) -> (fn [appender-data-map]) -> logging side effects."
|
||||||
|
[make-config] ; Any config that can influence the appender-fn construction
|
||||||
|
(let [{:keys []} make-config]
|
||||||
|
|
||||||
|
(fn [data] ; Data map as provided to middleware + appenders
|
||||||
|
(let [{:keys [instant level ?err_ vargs_ output-fn
|
||||||
|
|
||||||
|
config ; Entire config map in effect
|
||||||
|
appender ; Entire appender map in effect
|
||||||
|
|
||||||
|
;; = (:opts <appender-map>), for convenience. You'll
|
||||||
|
;; usually want to store+access runtime appender config
|
||||||
|
;; stuff here to let users change config without recreating
|
||||||
|
;; their appender fn:
|
||||||
|
appender-opts
|
||||||
|
|
||||||
|
;; <...>
|
||||||
|
;; See `timbre/example-config` for info on all available args
|
||||||
|
]}
|
||||||
|
data
|
||||||
|
|
||||||
|
{:keys [my-arbitrary-appender-opt1]} appender-opts
|
||||||
|
|
||||||
|
;;; Use `force` to realise possibly-delayed args:
|
||||||
|
?err (force ?err_) ; ?err non-nil iff first given arg was an error
|
||||||
|
vargs (force vargs_) ; Vector of raw args (excl. possible first error)
|
||||||
|
|
||||||
|
;; You'll often want a formatted string with ns, timestamp, vargs, etc.
|
||||||
|
;; A formatter (fn [logging-data-map & [opts]]) -> string is
|
||||||
|
;; provided for you under the :output-fn key. Prefer using this fn
|
||||||
|
;; to your own formatter when possible, since the user can
|
||||||
|
;; configure the :output-fn formatter in a standard way that'll
|
||||||
|
;; influence all participating appenders. It may help to look at
|
||||||
|
;; the source code for `taoensso.timbre/default-output-fn`!
|
||||||
|
;;
|
||||||
|
any-special-output-fn-opts {} ; Output-fn can use these opts
|
||||||
|
output-string (output-fn data any-special-output-fn-opts)]
|
||||||
|
|
||||||
|
(println (str my-arbitrary-appender-opt1 output-string))))))
|
||||||
|
|
||||||
|
(defn make-appender ; Prefer generic name to `make-foo-appender`, etc.
|
||||||
|
"Your docstring describing the appender, its options, etc."
|
||||||
|
[& [appender-config make-appender-config]]
|
||||||
|
(let [default-appender-config
|
||||||
|
{:doc "My appender docstring"
|
||||||
|
:enabled? true ; Please enable your appender by default
|
||||||
|
:min-level :debug
|
||||||
|
:rate-limit [[5 (encore/ms :mins 1)] ; 5 calls/min
|
||||||
|
[100 (encore/ms :hours 1)] ; 100 calls/hour
|
||||||
|
]
|
||||||
|
|
||||||
|
;; Any default appender-specific opts. These'll be accessible to your
|
||||||
|
;; appender fn under the :appender-opts key for convenience:
|
||||||
|
:opts {:my-arbitrary-appender-opt1 "hello world, "}}
|
||||||
|
|
||||||
|
;;; Here we'll prepare the final appender map as described in
|
||||||
|
;;; `timbre/example-config`:
|
||||||
|
appender-config (merge default-appender-config appender-config)
|
||||||
|
appender-fn (make-appender-fn make-appender-config)
|
||||||
|
appender (merge appender-config {:fn appender-fn})]
|
||||||
|
|
||||||
|
appender))
|
||||||
|
|
||||||
|
(comment
|
||||||
|
;; Your examples, tests, etc. here
|
||||||
|
)
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
;; Limitations inline
|
;; Limitations inline
|
||||||
(write! [_ level throwable message]
|
(write! [_ level throwable message]
|
||||||
(let [config *config* ; No support for explicit config
|
(let [config timbre/*config* ; No support for explicit config
|
||||||
?ns-str nil ; No support
|
?ns-str nil ; No support
|
||||||
?file nil ; ''
|
?file nil ; ''
|
||||||
?line nil ; ''
|
?line nil ; ''
|
||||||
|
|
Loading…
Reference in New Issue