From 8e5329e79f1d0b8fc76a6c1e622414ef32cd4f01 Mon Sep 17 00:00:00 2001 From: Peter Taoussanis Date: Sun, 1 Dec 2013 20:37:29 +0700 Subject: [PATCH] Add unified arg hashing (rate limiter, Carmine appender, etc.) --- src/taoensso/timbre.clj | 26 ++++++++++++++++++++--- src/taoensso/timbre/appenders/carmine.clj | 16 ++++---------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/taoensso/timbre.clj b/src/taoensso/timbre.clj index 56579e7..6e29c53 100644 --- a/src/taoensso/timbre.clj +++ b/src/taoensso/timbre.clj @@ -98,6 +98,7 @@ :rate-limit ; (Optional) [ncalls-limit window-ms]. :fmt-output-opts ; (Optional) extra opts passed to `fmt-output-fn`. :fn ; (fn [appender-args-map]), with keys described below. + :args-hash-fn ; Experimental. Used by rate-limiter, etc. An appender's fn takes a single map with keys: :level ; Keyword. @@ -179,10 +180,28 @@ ;;;; Appender-fn decoration +(defn default-args-hash-fn + "Alpha - subject to change!! + Returns a hash identifier for given appender arguments in such a way that + (= (hash args-A) (hash args-B)) iff arguments A and B are \"the same\" by + some reasonable-in-the-general-case definition for logging arguments. + + Useful in the context of rate limiting, deduplicating appenders, etc." + + ;; Things like dates & user ids user ids will still trip us up. + ;; `[hostname ns line]` may be another idea? + ;; Waiting on http://dev.clojure.org/jira/browse/CLJ-865. + + [{:keys [hostname ns args] :as apfn-args}] + (str (or (some #(and (map? %) (:timbre/hash %)) args) ; Explicit hash given + [hostname ns args]))) + (defn- wrap-appender-fn "Wraps compile-time appender fn with additional runtime capabilities controlled by compile-time config." - [config {apfn :fn :keys [async? rate-limit fmt-output-opts] :as appender}] + [config {:as appender apfn :fn + :keys [async? rate-limit fmt-output-opts args-hash-fn] + :or {args-hash-fn default-args-hash-fn}}] (let [rate-limit (or rate-limit ; Backwards comp: (if-let [x (:max-message-per-msecs appender)] [1 x] (when-let [x (:limit-per-msecs appender)] [1 x])))] @@ -208,12 +227,13 @@ limiter-any (utils/rate-limiter ncalls-limit window-ms) ;; This is a little hand-wavy but it's a decent general ;; strategy and helps us from making this overly complex to - ;; configure: + ;; configure. limiter-specific (utils/rate-limiter (quot ncalls-limit 4) window-ms)] (fn [{:keys [ns args] :as apfn-args}] ;; Runtime: (test smaller limit 1st): - (when-not (or (limiter-specific (str ns args)) (limiter-any)) + (when-not (or (limiter-specific (args-hash-fn apfn-args)) + (limiter-any)) (apfn apfn-args))))))) ;; Async (agent) support diff --git a/src/taoensso/timbre/appenders/carmine.clj b/src/taoensso/timbre/appenders/carmine.clj index 4aa787b..e9bc66d 100644 --- a/src/taoensso/timbre/appenders/carmine.clj +++ b/src/taoensso/timbre/appenders/carmine.clj @@ -17,14 +17,6 @@ (defn default-keyfn [level] {:pre [(string? level)]} (format "carmine:timbre:default:%s" level)) -(defn default-entry-hash-fn [{:keys [hostname ns args] :as apfn-args}] - ;; We try choose a hashing strategy here that gives a reasonable - ;; definition of 'uniqueness' for general entries. Things like dates - ;; or user ids will still trip us up. `[hostname ns line]` may be another - ;; idea? Waiting on http://dev.clojure.org/jira/browse/CLJ-865. - (or (some #(and (map? %) (:timbre/id %)) args) - [hostname ns args])) - (defn make-carmine-appender "Alpha - subject to change! Returns a Carmine Redis appender: @@ -38,9 +30,9 @@ also offer interesting opportunities here. See accompanying `query-entries` fn to return deserialized log entries." - [& [appender-opts {:keys [conn keyfn entry-hash-fn nentries-by-level] - :or {keyfn default-keyfn - entry-hash-fn default-entry-hash-fn + [& [appender-opts {:keys [conn keyfn args-hash-fn nentries-by-level] + :or {keyfn default-keyfn + args-hash-fn timbre/default-args-hash-fn nentries-by-level {:trace 50 :debug 50 :info 50 @@ -56,7 +48,7 @@ (merge default-appender-opts appender-opts {:fn (fn [{:keys [level instant] :as apfn-args}] - (let [entry-hash (sha48 (entry-hash-fn apfn-args)) + (let [entry-hash (sha48 (args-hash-fn apfn-args)) entry (select-keys apfn-args [:hostname :ns :args :throwable :profile-stats]) k-zset (keyfn (name level))