BREAKING: Clean up config structure. Add prefix-fn option.

Changes to config structure:
* [:shared-appender-config :timestamp-pattern] -> :timestamp-pattern
* [:shared-appender-config :locale]            -> :timestamp-locale
* Add :prefix-fn

Motivation: timestamp options aren't actually config intended for
appenders. Having them in :shared-appender-config was unnecessarily
confusing.

The addition of :prefix-fn allows full control of prefix strings in a
way that carries over automatically to all appenders. Previously,
adjusting prefix text required modifying appender fns.

Changes to default appenders:
* Drop [:appenders :standard-out]
* [:appenders :standard-out-or-err] -> [:appenders :standard-out]

Motivation: the :standard-out-or-err appender was already the default
anyway, and the presence of the :standard-out appender was just
confusing.
This commit is contained in:
Peter Taoussanis 2012-07-26 15:53:21 +07:00
parent 83c8ff19f7
commit f58442f355
2 changed files with 35 additions and 36 deletions

View File

@ -9,12 +9,6 @@
;;;; Default configuration and appenders
(defn prefixed-message
"timestamp hostname LEVEL [ns] - message"
[{:keys [level timestamp hostname ns message]}]
(str timestamp " " hostname " " (-> level name str/upper-case)
" [" ns "] - " message))
(defn str-println
"Like `println` but prints all objects to output stream as a single
atomic string. This is faster and avoids interleaving race conditions."
@ -31,38 +25,42 @@
:doc, :min-level, :enabled?, :async?, :max-message-per-msecs, :fn?
An appender's fn takes a single map argument with keys:
:ap-config, :level, :error?, :instant, :timestamp, :hostname, :ns,
:message, :more, :profiling-stats (when applicable)
:level, :message, :more ; From all logging macros (`info`, etc.)
:profiling-stats ; From `profile` macro
:ap-config ; `shared-appender-config`
:prefix ; Output of `prefix-fn`
Other keys include: :instant, :timestamp, :hostname, :ns, :error?
See source code for examples."}
(atom {:current-level :debug
;;; Allow log filtering by namespace patterns (e.g. ["my-app.*"]).
;;; Control log filtering by namespace patterns (e.g. ["my-app.*"]).
;;; Useful for turning off logging in noisy libraries, etc.
:ns-whitelist []
:ns-blacklist []
;;; Control :timestamp format
:timestamp-pattern "yyyy-MMM-dd HH:mm:ss ZZ" ; SimpleDateFormat pattern
:timestamp-locale nil ; A Locale object, or nil
;;; Control :prefix format
:prefix-fn
(fn [{:keys [level timestamp hostname ns]}]
(str timestamp " " hostname " " (-> level name str/upper-case)
" [" ns "]"))
;; Will be provided to all appenders via :ap-config key
:shared-appender-config {}
:appenders
{:standard-out
{:doc "Prints everything to *out*."
:min-level nil :enabled? false :async? false
:max-message-per-msecs nil
:fn (fn [{:keys [more] :as args}]
(apply str-println (prefixed-message args) more))}
:standard-out-or-err
{:doc "Prints to *out* or *err* as appropriate. Enabled by default."
:min-level nil :enabled? true :async? false
:max-message-per-msecs nil
:fn (fn [{:keys [error? more] :as args}]
:fn (fn [{:keys [error? prefix message more]}]
(binding [*out* (if error? *err* *out*)]
(apply str-println (prefixed-message args) more)))}}
;; Will be given to all appenders via :ap-config key
:shared-appender-config
{:timestamp-pattern "yyyy-MMM-dd HH:mm:ss ZZ" ; SimpleDateFormat pattern
:locale nil ; A Locale object, or nil
}}))
(apply str-println prefix "-" message more)))}}}))
(defn set-config! [[k & ks] val] (swap! config assoc-in (cons k ks) val))
(defn set-level! [level] (set-config! [:current-level] level))
@ -75,6 +73,8 @@
[x] (when-not (some #{x} ordered-levels)
(throw (Exception. (str "Invalid logging level: " x)))))
(defn error-level? [x] (boolean (#{:error :fatal} x)))
(def compare-levels
(memoize (fn [x y] (- (scored-levels x) (scored-levels y)))))
@ -96,8 +96,7 @@
(def get-hostname
(utils/memoize-ttl
60000
(fn [] (.. java.net.InetAddress getLocalHost getHostName))))
60000 (fn [] (.. java.net.InetAddress getLocalHost getHostName))))
(defn- wrap-appender-fn
"Wraps compile-time appender fn with additional runtime capabilities
@ -105,15 +104,15 @@
[{apfn :fn :keys [async? max-message-per-msecs] :as appender}]
(->
;; Wrap to add compile-time stuff to runtime appender arguments
(let [{:keys [timestamp-pattern locale] :as ap-config}
(@config :shared-appender-config)
timestamp-fn (make-timestamp-fn timestamp-pattern locale)]
(let [{:keys [timestamp-pattern timestamp-locale prefix-fn] :as ap-config}
@config
timestamp-fn (make-timestamp-fn timestamp-pattern timestamp-locale)]
(fn [{:keys [instant] :as apfn-args}]
(apfn (assoc apfn-args
:ap-config ap-config
:timestamp (timestamp-fn instant)
:hostname (get-hostname)))))
(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]
@ -244,7 +243,7 @@
(conj
~base-args ; Allow flexibility to inject exta args
{:level ~level
:error? (>= (compare-levels ~level :error) 0)
:error? (error-level? ~level)
:instant (Date.)
:ns (str ~*ns*)
:message (if has-throwable?# (or (first xs#) x1#) x1#)

View File

@ -13,10 +13,10 @@
{:from \"Bob's logger <me@draines.com>\" :to \"foo@example.com\"}")
:min-level :error :enabled? true :async? true
:max-message-per-msecs (* 1000 60 10) ; 1 email per message per 10 mins
:fn (fn [{:keys [ap-config more] :as args}]
:fn (fn [{:keys [ap-config prefix message more]}]
(when-let [postal-config (:postal ap-config)]
(postal/send-message
(assoc postal-config
:subject (timbre/prefixed-message args)
:subject (str prefix " - " message)
:body (if (seq more) (str/join " " more)
"<no additional arguments>")))))})