NB migrate utils stuff to `encore` lib

This commit is contained in:
Peter Taoussanis 2014-02-23 19:46:49 +07:00
parent f7b2615ae6
commit 7355cd6424
6 changed files with 28 additions and 132 deletions

View File

@ -41,8 +41,7 @@ The `refer-timbre` call is a convenience fn that executes:
(require '[taoensso.timbre :as timbre
:refer (log trace debug info warn error fatal report
logf tracef debugf infof warnf errorf fatalf reportf
spy logged-future with-log-level)])
(require '[taoensso.timbre.utils :refer (sometimes)])
spy logged-future with-log-level sometimes)])
(require '[taoensso.timbre.profiling :as profiling :refer (pspy profile defnp)])
```

View File

@ -10,9 +10,9 @@
:global-vars {*warn-on-reflection* true
*assert* true}
:dependencies
[[org.clojure/clojure "1.4.0"]
[org.clojure/tools.macro "0.1.5"]
[io.aviso/pretty "0.1.8"]]
[[org.clojure/clojure "1.4.0"]
[io.aviso/pretty "0.1.8"]
[com.taoensso/encore "0.8.0"]]
:test-paths ["test" "src"]
:profiles

View File

@ -2,7 +2,7 @@
{:author "Peter Taoussanis"}
(:require [clojure.string :as str]
[io.aviso.exception :as aviso-ex]
[taoensso.timbre.utils :as utils])
[taoensso.encore :as encore])
(:import [java.util Date Locale]
[java.text SimpleDateFormat]))
@ -42,6 +42,12 @@
(comment (stacktrace (Exception. "foo") nil {}))
(defmacro sometimes
"Executes body with probability e/o [0,1]. Useful for sampled logging."
[probability & body]
`(do (assert (<= 0 ~probability 1) "Probability: 0 <= p <= 1")
(when (< (rand) ~probability) ~@body)))
;;;; Logging levels
(def level-compile-time
@ -171,9 +177,9 @@
(try (spit filename (str output "\n") :append true)
(catch java.io.IOException _))))}}})
(utils/defonce* config (atom example-config))
(encore/defonce* config (atom example-config))
(defn set-config! [ks val] (swap! config assoc-in ks val))
(defn merge-config! [& maps] (apply swap! config utils/merge-deep maps))
(defn merge-config! [& maps] (apply swap! config encore/merge-deep maps))
;;;; Appender-fn decoration
@ -221,11 +227,11 @@
;; Compile-time:
(if-not rate-limit apfn
(let [[ncalls-limit window-ms] rate-limit
limiter-any (utils/rate-limiter ncalls-limit window-ms)
limiter-any (encore/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.
limiter-specific (utils/rate-limiter (quot ncalls-limit 4)
limiter-specific (encore/rate-limiter (quot ncalls-limit 4)
window-ms)]
(fn [{:keys [ns args] :as apfn-args}]
;; Runtime: (test smaller limit 1st):
@ -256,7 +262,7 @@
(comment ((make-timestamp-fn "yyyy-MMM-dd" nil) (Date.)))
(def ^:private get-hostname
(utils/memoize-ttl 60000
(encore/memoize* 60000
(fn []
(let [p (promise)]
(future ; Android doesn't like this on the main thread
@ -499,8 +505,7 @@
'[taoensso.timbre :as timbre
:refer (log trace debug info warn error fatal report
logf tracef debugf infof warnf errorf fatalf reportf
spy logged-future with-log-level)])
(require '[taoensso.timbre.utils :refer (sometimes)])
spy logged-future with-log-level sometimes)])
(require
'[taoensso.timbre.profiling :as profiling :refer (pspy profile defnp)])"
[]
@ -508,8 +513,7 @@
'[taoensso.timbre :as timbre
:refer (log trace debug info warn error fatal report
logf tracef debugf infof warnf errorf fatalf reportf
spy logged-future with-log-level)])
(require '[taoensso.timbre.utils :refer (sometimes)])
spy logged-future with-log-level sometimes)])
(require
'[taoensso.timbre.profiling :as profiling :refer (pspy profile defnp)]))

View File

@ -2,8 +2,8 @@
"Logging profiler for Timbre, adapted from clojure.contrib.profile."
{:author "Peter Taoussanis"}
(:require [clojure.tools.macro :as macro]
[taoensso.timbre :as timbre]
[taoensso.timbre.utils :as utils]))
[taoensso.encore :as encore]
[taoensso.timbre :as timbre]))
(def ^:dynamic *pdata* "{::pname [time1 time2 ...]}" nil)
@ -12,7 +12,7 @@
of named body. Always returns the body's result."
[name & body]
`(if-not *pdata* (do ~@body)
(let [name# (utils/fq-keyword ~name)
(let [name# (encore/fq-keyword ~name)
start-time# (System/nanoTime)]
(try (do ~@body)
(finally
@ -42,7 +42,7 @@
`(let [{result# :result stats# :stats} (with-pdata ~level ~@body)]
(when stats#
(timbre/log* {:profile-stats stats#} :format ~level
"Profiling: %s\n%s" (utils/fq-keyword ~name)
"Profiling: %s\n%s" (encore/fq-keyword ~name)
(format-pdata stats#)))
result#))
@ -86,7 +86,7 @@
ft (fn [nanosecs]
(let [pow #(Math/pow 10 %)
ok-pow? #(>= nanosecs (pow %))
to-pow #(utils/round-to %2 (/ nanosecs (pow %1)))]
to-pow #(encore/round %2 :round (/ nanosecs (pow %1)))]
(cond (ok-pow? 9) (str (to-pow 9 1) "s")
(ok-pow? 6) (str (to-pow 6 0) "ms")
(ok-pow? 3) (str (to-pow 3 0) "μs")

View File

@ -1,109 +1,2 @@
(ns taoensso.timbre.utils
{:author "Peter Taoussanis"}
(:require [clojure.tools.macro :as macro]))
(defmacro defonce*
"Like `clojure.core/defonce` but supports optional docstring and attributes
map for name symbol."
{:arglists '([name expr])}
[name & sigs]
(let [[name [expr]] (macro/name-with-attributes name sigs)]
`(clojure.core/defonce ~name ~expr)))
(defn memoize-ttl "Low-overhead, common-case `memoize*`."
[ttl-ms f]
(let [cache (atom {})]
(fn [& args]
(when (<= (rand) 0.001) ; GC
(let [instant (System/currentTimeMillis)]
(swap! cache
(fn [m] (reduce-kv (fn [m* k [dv udt :as cv]]
(if (> (- instant udt) ttl-ms) m*
(assoc m* k cv))) {} m)))))
(let [[dv udt] (@cache args)]
(if (and dv (< (- (System/currentTimeMillis) udt) ttl-ms)) @dv
(locking cache ; For thread racing
(let [[dv udt] (@cache args)] ; Retry after lock acquisition!
(if (and dv (< (- (System/currentTimeMillis) udt) ttl-ms)) @dv
(let [dv (delay (apply f args))
cv [dv (System/currentTimeMillis)]]
(swap! cache assoc args cv)
@dv)))))))))
(defn rate-limiter
"Returns a `(fn [& [id]])` that returns either `nil` (limit okay) or number of
msecs until next rate limit window (rate limited)."
[ncalls-limit window-ms]
(let [state (atom [nil {}])] ; [<pull> {<id> {[udt-window-start ncalls]}}]
(fn [& [id]]
(when (<= (rand) 0.001) ; GC
(let [instant (System/currentTimeMillis)]
(swap! state
(fn [[_ m]]
[nil (reduce-kv
(fn [m* id [udt-window-start ncalls]]
(if (> (- instant udt-window-start) window-ms) m*
(assoc m* id [udt-window-start ncalls]))) {} m)]))))
(->
(let [instant (System/currentTimeMillis)]
(swap! state
(fn [[_ m]]
(if-let [[udt-window-start ncalls] (m id)]
(if (> (- instant udt-window-start) window-ms)
[nil (assoc m id [instant 1])]
(if (< ncalls ncalls-limit)
[nil (assoc m id [udt-window-start (inc ncalls)])]
[(- (+ udt-window-start window-ms) instant) m]))
[nil (assoc m id [instant 1])]))))
(nth 0)))))
(comment
(def rl (rate-limit 10 10000))
(repeatedly 10 #(rl (rand-nth [:a :b :c])))
(rl :a)
(rl :b)
(rl :c))
(defn merge-deep-with ; From clojure.contrib.map-utils
"Like `merge-with` but merges maps recursively, applying the given fn
only when there's a non-map at a particular level.
(merge-deep-with + {:a {:b {:c 1 :d {:x 1 :y 2}} :e 3} :f 4}
{:a {:b {:c 2 :d {:z 9} :z 3} :e 100}})
=> {:a {:b {:z 3, :c 3, :d {:z 9, :x 1, :y 2}}, :e 103}, :f 4}"
[f & maps]
(apply
(fn m [& maps]
(if (every? map? maps)
(apply merge-with m maps)
(apply f maps)))
maps))
(def merge-deep (partial merge-deep-with (fn [x y] y)))
(comment (merge-deep {:a {:b {:c {:d :D :e :E}}}}
{:a {:b {:g :G :c {:c {:f :F}}}}}))
(defn round-to "Rounds argument to given number of decimal places."
[places x]
(if (zero? places)
(Math/round (double x))
(let [modifier (Math/pow 10.0 places)]
(/ (Math/round (* x modifier)) modifier))))
(comment (round-to 0 10)
(round-to 2 10.123))
(defmacro fq-keyword "Returns namespaced keyword for given name."
[name]
`(if (and (keyword? ~name) (namespace ~name)) ~name
(keyword (str ~*ns*) (clojure.core/name ~name))))
(comment (map #(fq-keyword %) ["foo" :foo :foo/bar]))
(defmacro sometimes "Executes body with probability e/o [0,1]."
[probability & body]
`(do (assert (<= 0 ~probability 1) "Probability: 0 <= p <= 1")
(when (< (rand) ~probability) ~@body)))
{:author "Peter Taoussanis"})

View File

@ -1,10 +1,10 @@
(ns taoensso.timbre.tests.main
(:require [expectations :as test :refer :all]
[taoensso.timbre :as timbre :refer (trace debug info warn
error fatal spy)]
[taoensso.timbre.profiling :as profiling :refer (p profile)]))
[taoensso.timbre :as timbre]))
(timbre/refer-timbre)
(defn- before-run {:expectations-options :before-run} [])
(defn- after-run {:expectations-options :after-run} [])
(expect true) ; TODO Add tests (PRs welcome!)
(expect true) ; TODO Add tests (PRs welcome!)