mirror of https://github.com/status-im/timbre.git
Clean up postal appender, add new Carmine (Redis) appender
This commit is contained in:
parent
0a0bce8b69
commit
d75a2db4dd
|
@ -62,7 +62,7 @@
|
||||||
|
|
||||||
;;;
|
;;;
|
||||||
|
|
||||||
(def ^:private levels-ordered [:trace :debug :info :warn :error :fatal :report])
|
(def levels-ordered [:trace :debug :info :warn :error :fatal :report])
|
||||||
(def ^:private levels-scored (assoc (zipmap levels-ordered (next (range))) nil 0))
|
(def ^:private levels-scored (assoc (zipmap levels-ordered (next (range))) nil 0))
|
||||||
|
|
||||||
(defn error-level? [level] (boolean (#{:error :fatal} level))) ; For appenders, etc.
|
(defn error-level? [level] (boolean (#{:error :fatal} level))) ; For appenders, etc.
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
(ns taoensso.timbre.appenders.carmine
|
||||||
|
"Carmine (Redis) appender. Requires https://github.com/ptaoussanis/carmine."
|
||||||
|
{:author "Peter Taoussanis"}
|
||||||
|
(:require [taoensso.carmine :as car]
|
||||||
|
[taoensso.timbre :as timbre]))
|
||||||
|
|
||||||
|
(defn default-keyfn [level] {:pre [(string? level)]}
|
||||||
|
(format "carmine:timbre:default:%s" level))
|
||||||
|
|
||||||
|
(defn make-carmine-appender
|
||||||
|
"Alpha - subject to change!
|
||||||
|
Returns a Carmine Redis appender that logs serialized entries as follows:
|
||||||
|
* Logs only the most recent instance of each unique entry.
|
||||||
|
* Limits the number of entries per level (configurable).
|
||||||
|
* Sorts entries by date of most recent occurence.
|
||||||
|
|
||||||
|
See accompanying `query-entries` fn to return deserialized log entries."
|
||||||
|
[& [appender-opts {:keys [conn keyfn nentries-by-level]
|
||||||
|
:or {conn {}
|
||||||
|
keyfn default-keyfn
|
||||||
|
nentries-by-level {:trace 20
|
||||||
|
:debug 20
|
||||||
|
:info 50
|
||||||
|
:warn 100
|
||||||
|
:error 300
|
||||||
|
:fatal 500
|
||||||
|
:report 500}}}]]
|
||||||
|
{:pre [(string? (keyfn "debug"))
|
||||||
|
(every? #(contains? nentries-by-level %) timbre/levels-ordered)
|
||||||
|
(every? #(and (integer? %) (<= 0 % 100000)) (vals nentries-by-level))]}
|
||||||
|
|
||||||
|
(let [default-appender-opts {:enabled? true :min-level nil}]
|
||||||
|
(merge default-appender-opts appender-opts
|
||||||
|
{:fn
|
||||||
|
(fn [{:keys [level instant] :as apfn-args}]
|
||||||
|
(let [k (keyfn (name level))
|
||||||
|
nmax-entries (nentries-by-level level)
|
||||||
|
;; Note that we _exclude_ :instant for uniqueness and efficiency
|
||||||
|
;; (we'll use it as zset score):
|
||||||
|
entry (select-keys apfn-args [:level :throwable :args
|
||||||
|
:profile-stats :hostname :ns])
|
||||||
|
udt (.getTime ^java.util.Date instant)]
|
||||||
|
(car/wcar conn
|
||||||
|
(car/zadd k udt entry)
|
||||||
|
(car/zremrangebyrank k 0 (dec (- nmax-entries))))))})))
|
||||||
|
|
||||||
|
;;;; Query utils
|
||||||
|
|
||||||
|
(defn query-entries
|
||||||
|
"Alpha - subject to change!
|
||||||
|
Returns latest `n` log entries by level as an ordered vector of deserialized
|
||||||
|
maps. Normal sequence fns can be used to query/transform entries."
|
||||||
|
[conn level & [n asc? keyfn]]
|
||||||
|
{:pre [(or (nil? n) (and (integer? n) (<= 1 n 100000)))]}
|
||||||
|
(let [keyfn (or keyfn default-keyfn)
|
||||||
|
k (keyfn (name level))]
|
||||||
|
(->>
|
||||||
|
(car/wcar conn
|
||||||
|
(if asc? (car/zrange k 0 (if n (dec n) -1) :withscores)
|
||||||
|
(car/zrevrange k 0 (if n (dec n) -1) :withscores)))
|
||||||
|
;; Reconstitute :instant keys from scores:
|
||||||
|
(partition 2)
|
||||||
|
(reduce (fn [v [m-entry score]]
|
||||||
|
(conj v (assoc m-entry :instant (car/as-long score))))
|
||||||
|
[]))))
|
||||||
|
|
||||||
|
;;;; Dev/tests
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(timbre/log {:timestamp-pattern "yyyy-MMM-dd HH:mm:ss ZZ"
|
||||||
|
:appenders {:carmine (make-carmine-appender)}}
|
||||||
|
:info "Hello1" "Hello2")
|
||||||
|
|
||||||
|
(car/wcar {} (car/del (default-keyfn "info")))
|
||||||
|
(car/wcar {} (car/keys (default-keyfn "*")))
|
||||||
|
(count (query-entries {} :info 2 :asc)))
|
|
@ -1,5 +1,5 @@
|
||||||
(ns taoensso.timbre.appenders.postal
|
(ns taoensso.timbre.appenders.postal
|
||||||
"Email appender. Depends on https://github.com/drewr/postal."
|
"Email appender. Requires https://github.com/drewr/postal."
|
||||||
{:author "Peter Taoussanis"}
|
{:author "Peter Taoussanis"}
|
||||||
(:require [clojure.string :as str]
|
(:require [clojure.string :as str]
|
||||||
[postal.core :as postal]
|
[postal.core :as postal]
|
||||||
|
@ -11,17 +11,37 @@
|
||||||
|
|
||||||
(comment (str-trunc "Hello this is a long string" 5))
|
(comment (str-trunc "Hello this is a long string" 5))
|
||||||
|
|
||||||
(def postal-appender
|
(defn make-postal-appender
|
||||||
{:doc (str "Sends an email using com.draines/postal.\n"
|
"Returns a Postal email appender.
|
||||||
"Needs :postal config map in :shared-appender-config, e.g.:
|
A Postal config map can be provided here as an argument, or as a :postal key
|
||||||
|
in :shared-appender-config.
|
||||||
|
|
||||||
|
(make-postal-appender {:enabled? true}
|
||||||
|
{:postal-config
|
||||||
^{:host \"mail.isp.net\" :user \"jsmith\" :pass \"sekrat!!1\"}
|
^{:host \"mail.isp.net\" :user \"jsmith\" :pass \"sekrat!!1\"}
|
||||||
{:from \"Bob's logger <me@draines.com>\" :to \"foo@example.com\"}")
|
{:from \"Bob's logger <me@draines.com>\" :to \"foo@example.com\"}})"
|
||||||
:min-level :error :enabled? true :async? true
|
[& [appender-opts {:keys [postal-config subject-len]
|
||||||
:fmt-output-opts {:nofonts? true}
|
:or {subject-len 150}}]]
|
||||||
|
|
||||||
|
(let [default-appender-opts
|
||||||
|
{:enabled? true
|
||||||
|
:min-level :warn
|
||||||
|
:async? true ; Slow!
|
||||||
:rate-limit [5 (* 1000 60 2)] ; 5 calls / 2 mins
|
:rate-limit [5 (* 1000 60 2)] ; 5 calls / 2 mins
|
||||||
:fn (fn [{:keys [ap-config output]}]
|
:fmt-output-opts {:nofonts? true} ; Disable ANSI-escaped stuff
|
||||||
(when-let [postal-config (:postal ap-config)]
|
}]
|
||||||
|
|
||||||
|
(merge default-appender-opts appender-opts
|
||||||
|
{:fn
|
||||||
|
(fn [{:keys [ap-config output]}]
|
||||||
|
(when-let [postal-config (or postal-config (:postal ap-config))]
|
||||||
(postal/send-message
|
(postal/send-message
|
||||||
(assoc postal-config
|
(assoc postal-config
|
||||||
:subject (-> output (str/trim) (str-trunc 150) (str/replace #"\s+" " "))
|
:subject (-> (str output)
|
||||||
:body output))))})
|
(str/trim)
|
||||||
|
(str-trunc subject-len)
|
||||||
|
(str/replace #"\s+" " "))
|
||||||
|
:body output))))})))
|
||||||
|
|
||||||
|
(def postal-appender "DEPRECATED: Use `make-postal-appender` instead."
|
||||||
|
(make-postal-appender))
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
(ns taoensso.timbre.appenders.rotor
|
(ns taoensso.timbre.appenders.rotor
|
||||||
|
{:author "Yutaka Matsubara"}
|
||||||
(:import
|
(:import
|
||||||
[java.io File FilenameFilter])
|
[java.io File FilenameFilter])
|
||||||
(:require
|
(:require
|
||||||
|
|
Loading…
Reference in New Issue