Clean up postal appender, add new Carmine (Redis) appender

This commit is contained in:
Peter Taoussanis 2013-11-30 17:38:14 +07:00
parent 0a0bce8b69
commit d75a2db4dd
4 changed files with 112 additions and 15 deletions

View File

@ -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.

View File

@ -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)))

View File

@ -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))

View File

@ -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