Rename appender argument: :max-message-per-msecs -> :limit-per-msecs (backwards compatible)

This commit is contained in:
Peter Taoussanis 2013-05-15 22:37:02 +07:00
parent c30ae5fbcc
commit 6fc6baee37
4 changed files with 54 additions and 55 deletions

View File

@ -16,7 +16,7 @@ Timbre is an attempt to make **simple logging simple** and more **complex loggin
* **Decent performance** (low overhead).
* Flexible **fn-centric appender model** with **middleware**.
* Sensible built-in appenders including simple **email appender**.
* Tunable **flood control** and **asynchronous** logging support.
* Tunable **rate limit** and **asynchronous** logging support.
* Robust **namespace filtering**.
* Dead-simple, logging-level-aware **logging profiler**.
@ -137,8 +137,8 @@ Filter logging output by namespaces:
^{:host "mail.isp.net" :user "jsmith" :pass "sekrat!!1"}
{:from "me@draines.com" :to "foo@example.com"})
;; Rate-limit to one email per message per minute
(timbre/set-config! [:appenders :postal :max-message-per-msecs] 60000)
;; Rate limit to one email per message per minute
(timbre/set-config! [:appenders :postal :limit-per-msecs] 60000)
;; Make sure emails are sent asynchronously
(timbre/set-config! [:appenders :postal :async?] true)
@ -170,7 +170,7 @@ Writing a custom appender is dead-easy:
:min-level :debug
:enabled? true
:async? false
:max-message-per-msecs nil ; No rate limiting
:limit-per-msecs nil ; No rate limit
:fn (fn [{:keys [ap-config level prefix message more] :as args}]
(when-not (:my-production-mode? ap-config)
(apply println prefix "Hello world!" message more)))

View File

@ -45,7 +45,7 @@
APPENDERS
An appender is a map with keys:
:doc, :min-level, :enabled?, :async?, :max-message-per-msecs, :fn
:doc, :min-level, :enabled?, :async?, :limit-per-msecs, :fn
An appender's fn takes a single map argument with keys:
:level, :message, :more ; From all logging macros (`info`, etc.)
@ -88,16 +88,14 @@
:appenders
{:standard-out
{:doc "Prints to *out* or *err* as appropriate. Enabled by default."
:min-level nil :enabled? true :async? false
:max-message-per-msecs nil
:min-level nil :enabled? true :async? false :limit-per-msecs nil
:fn (fn [{:keys [error? prefix message more]}]
(binding [*out* (if error? *err* *out*)]
(apply str-println prefix "-" message more)))}
:spit
{:doc "Spits to (:spit-filename :shared-appender-config) file."
:min-level nil :enabled? false :async? false
:max-message-per-msecs nil
:min-level nil :enabled? false :async? false :limit-per-msecs nil
:fn (fn [{:keys [ap-config prefix message more]}]
(when-let [filename (:spit-filename ap-config)]
(try (spit filename
@ -132,53 +130,55 @@
(defn- wrap-appender-fn
"Wraps compile-time appender fn with additional runtime capabilities
controlled by compile-time config."
[{apfn :fn :keys [async? max-message-per-msecs prefix-fn] :as appender}]
(->> ; Wrapping applies per appender, bottom-to-top
apfn
[{apfn :fn :keys [async? limit-per-msecs prefix-fn] :as appender}]
(let [limit-per-msecs (or (:max-message-per-msecs appender)
limit-per-msecs)] ; Backwards-compatibility
(->> ; Wrapping applies per appender, bottom-to-top
apfn
;; Prefix-fn support
((fn [apfn]
(if-not prefix-fn
apfn
(fn [apfn-args]
(apfn (assoc apfn-args
:prefix (prefix-fn apfn-args)))))))
;; Per-appender prefix-fn support (cmp. default prefix-fn)
((fn [apfn]
(if-not prefix-fn
apfn
(fn [apfn-args]
(apfn (assoc apfn-args
:prefix (prefix-fn apfn-args)))))))
;; Rate limit support
((fn [apfn]
(if-not max-message-per-msecs
apfn
(let [;; {:hash last-appended-time-msecs ...}
flood-timers (atom {})]
;; Rate limit support
((fn [apfn]
(if-not limit-per-msecs
apfn
(let [;; {:hash last-appended-time-msecs ...}
flood-timers (atom {})]
(fn [{:keys [ns message] :as apfn-args}]
(let [now (System/currentTimeMillis)
hash (str ns "/" message)
allow? (fn [last-msecs]
(or (not last-msecs)
(> (- now last-msecs) max-message-per-msecs)))]
(fn [{:keys [ns message] :as apfn-args}]
(let [now (System/currentTimeMillis)
hash (str ns "/" message)
allow? (fn [last-msecs]
(or (not last-msecs)
(> (- now last-msecs) limit-per-msecs)))]
(when (allow? (@flood-timers hash))
(apfn apfn-args)
(swap! flood-timers assoc hash now))
(when (allow? (@flood-timers hash))
(apfn apfn-args)
(swap! flood-timers assoc hash now))
;; Occassionally garbage-collect all expired timers. Note
;; that due to snapshotting, garbage-collection can cause
;; some appenders to re-append prematurely.
(when (< (rand) 0.001)
(let [timers-snapshot @flood-timers
expired-timers
(->> (keys timers-snapshot)
(filter #(allow? (timers-snapshot %))))]
(when (seq expired-timers)
(apply swap! flood-timers dissoc expired-timers))))))))))
;; Occassionally garbage-collect all expired timers. Note
;; that due to snapshotting, garbage-collection can cause
;; some appenders to re-append prematurely.
(when (< (rand) 0.001)
(let [timers-snapshot @flood-timers
expired-timers
(->> (keys timers-snapshot)
(filter #(allow? (timers-snapshot %))))]
(when (seq expired-timers)
(apply swap! flood-timers dissoc expired-timers))))))))))
;; 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))))))))))
;; 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
@ -410,4 +410,4 @@
(if (contains? message :password)
(assoc args :message (assoc message :password "*****"))
args)
:else args))]))
:else args))]))

View File

@ -39,7 +39,6 @@
"Needs :irc config map in :shared-appender-config, e.g.:
{:host \"irc.example.org\" :port 6667 :nick \"logger\"
:name \"My Logger\" :chan \"#logs\"")
:min-level :info :enabled? true :async? false
:max-message-per-msecs nil ; no rate limit by default
:min-level :info :enabled? true :async? false :limit-per-msecs nil
:prefix-fn (fn [{:keys [level]}] (-> level name str/upper-case))
:fn appender-fn})

View File

@ -11,11 +11,11 @@
^{:host \"mail.isp.net\" :user \"jsmith\" :pass \"sekrat!!1\"}
{: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
:limit-per-msecs (* 1000 60 10) ; 1 subject / 10 mins
:fn (fn [{:keys [ap-config prefix message more]}]
(when-let [postal-config (:postal ap-config)]
(postal/send-message
(assoc postal-config
:subject (str prefix " - " message)
:body (if (seq more) (str/join " " more)
"<no additional arguments>")))))})
"<no additional arguments>")))))})