Merge branch 'dev'

This commit is contained in:
Peter Taoussanis 2014-09-02 17:58:35 +07:00
commit fb44efddd8
7 changed files with 178 additions and 93 deletions

View File

@ -1,3 +1,14 @@
> This project uses [Break Versioning](https://github.com/ptaoussanis/encore/blob/master/BREAK-VERSIONING.md) as of **Aug 16, 2014**.
## v3.3.0 / 2014 May 8
* **CHANGE**: Update IRC appender to Timbre v3 style (@crisptrutski).
* **FIX** [#47]: correctly format nanosecond profiling times.
* **FIX** [#77]: profile ids now use correct (compile-time rather than runtime) ns prefix.
* **NEW**: Add zmq appender (@angusiguess).
* **NEW** [#75]: Make defnp support multi-arity functions (@maurolopes)
## v3.2.1 / 2014 May 7
* **FIX**: missing tools.reader upstream dependency (@ducky427).
@ -5,7 +16,7 @@
## v3.2.0 / 2014 May 6
* [#60] **FIX**: `defnp` no longer generates an Eastwood warning (@ducky427).
* **FIX** [#60]: `defnp` no longer generates an Eastwood warning (@ducky427).
* **CHANGE**: Improved profiling memory efficiency (max memory use, was previously unbounded).
* **CHANGE**: Profiling: make larger call numbers easier to read.
* [#63]: **NEW**: Add support for thread-local configuration (@jameswarren).
@ -13,19 +24,19 @@
## v3.1.6 / 2014 Mar 16
* [#56] FIX: `defnp`/`p` head retention issue (kyptin).
* **FIX** [#56]: `defnp`/`p` head retention issue (@kyptin).
## v3.1.5 / 2014 Mar 15
* FIX: `profiling/p*` was defined incorrectly (kyptin).
* **FIX**: `profiling/p*` was defined incorrectly (@kyptin).
## v3.1.4 / 2014 Mar 13
* NEW: Add `profiling/p*` macro.
* CHANGE: Include `p`, `p*` in `refer-timbre` imports.
* FIX: rotor appender not rotating (iantruslove, kurtharriger).
* **NEW**: Add `profiling/p*` macro.
* **CHANGE**: Include `p`, `p*` in `refer-timbre` imports.
* **FIX**: rotor appender not rotating (@iantruslove, @kurtharriger).
## v3.1.3 / 2014 Mar 11

View File

@ -1,7 +1,7 @@
**[API docs][]** | **[CHANGELOG][]** | [other Clojure libs][] | [Twitter][] | [contact/contributing](#contact--contributing) | current ([semantic][]) version:
**[API docs][]** | **[CHANGELOG][]** | [other Clojure libs][] | [Twitter][] | [contact/contrib](#contact--contributing) | current [Break Version][]:
```clojure
[com.taoensso/timbre "3.2.1"] ; Stable
[com.taoensso/timbre "3.3.0"]
```
v3 is a **major, backwards-compatible release**. Please see the [CHANGELOG][] for details. Appender authors: please see [here](https://github.com/ptaoussanis/timbre/issues/41) about migrating Timbre 2.x appenders to 3.x's recommended style.
@ -34,7 +34,7 @@ Logging with Java can be maddeningly, unnecessarily hard. Particularly if all yo
Add the necessary dependency to your [Leiningen][] `project.clj` and use the supplied ns-import helper:
```clojure
[com.taoensso/timbre "3.2.1"] ; project.clj
[com.taoensso/timbre "3.3.0"] ; project.clj
(ns my-app (:require [taoensso.timbre :as timbre])) ; Your ns
(timbre/refer-timbre) ; Provides useful Timbre aliases in this ns
@ -295,7 +295,8 @@ Copyright © 2012-2014 Peter Taoussanis. Distributed under the [Eclipse Publ
[CHANGELOG]: <https://github.com/ptaoussanis/timbre/releases>
[other Clojure libs]: <https://www.taoensso.com/clojure-libraries>
[Twitter]: <https://twitter.com/ptaoussanis>
[semantic]: <http://semver.org/>
[SemVer]: <http://semver.org/>
[Break Version]: <https://github.com/ptaoussanis/encore/blob/master/BREAK-VERSIONING.md>
[Leiningen]: <http://leiningen.org/>
[CDS]: <http://clojure-doc.org/>
[ClojureWerkz]: <http://clojurewerkz.org/>

View File

@ -1,4 +1,4 @@
(defproject com.taoensso/timbre "3.2.1"
(defproject com.taoensso/timbre "3.3.0"
:author "Peter Taoussanis <https://www.taoensso.com>"
:description "Clojure logging & profiling library"
:url "https://github.com/ptaoussanis/timbre"
@ -9,42 +9,39 @@
:min-lein-version "2.3.3"
:global-vars {*warn-on-reflection* true
*assert* true}
:dependencies
[[org.clojure/clojure "1.4.0"]
[com.taoensso/encore "1.5.1"]
[io.aviso/pretty "0.1.10"]]
[com.taoensso/encore "1.7.3"]
[io.aviso/pretty "0.1.12"]]
:test-paths ["test" "src"]
:profiles
{;; :default [:base :system :user :provided :dev]
:server-jvm {:jvm-opts ^:replace ["-server"]}
:1.5 {:dependencies [[org.clojure/clojure "1.5.1"]]}
:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]}
:test {:dependencies [[expectations "1.4.56"]
[org.clojure/test.check "0.5.7"]
[com.taoensso/nippy "2.6.3"]
[com.taoensso/carmine "2.6.2"]
[com.draines/postal "1.11.1"]
[org.clojure/tools.logging "0.2.6"]]
:test {:dependencies [[expectations "2.0.9"]
[org.clojure/test.check "0.5.9"]
[com.taoensso/nippy "2.7.0-RC1"]
[com.taoensso/carmine "2.7.0"]
[com.draines/postal "1.11.1"]
[org.clojure/tools.logging "0.3.0"]]
:plugins [[lein-expectations "0.0.8"]
[lein-autoexpect "1.2.2"]]}
:dev* [:dev {:jvm-opts ^:replace ["-server"]
;; :hooks [cljx.hooks leiningen.cljsbuild] ; cljx
}]
:dev
[:1.6 :test
{:dependencies []
{:dependencies [[irclj "0.5.0-alpha4"]]
:plugins [[lein-ancient "0.5.4"]
[codox "0.6.7"]]}]}
[codox "0.8.10"]]}]}
:test-paths ["test" "src"]
;; :codox {:sources ["target/classes"]} ; cljx
:aliases
{"test-all" ["with-profile" "default:+1.5:+1.6" "expectations"]
;; "test-all" ["with-profile" "default:+1.6" "expectations"]
"test-auto" ["with-profile" "+test" "autoexpect"]
;; "build-once" ["do" "cljx" "once," "cljsbuild" "once"] ; cljx
;; "deploy-lib" ["do" "build-once," "deploy" "clojars," "install"] ; cljx
"deploy-lib" ["do" "deploy" "clojars," "install"]
"start-dev" ["with-profile" "+dev*" "repl" ":headless"]}
"start-dev" ["with-profile" "+server-jvm" "repl" ":headless"]}
:repositories
{"sonatype"

View File

@ -283,6 +283,9 @@
(let [juxtfn-args (if-not msg-type juxtfn-args ; tools.logging
(-> juxtfn-args
(dissoc :msg-type)
;; TODO Consider a breaking change here to
;; swap assoc'd message with a delay, as
;; per http://goo.gl/7YVSfj:
(assoc :message
(when-not (empty? args)
(case msg-type
@ -403,6 +406,9 @@
}))
nil))
(defmacro get-compile-time-ns [] (str *ns*)) ; Nb need `str` to be readable
(comment (macroexpand '(get-compile-time-ns)))
(defmacro log* "Implementation detail."
{:arglists '([base-appender-args msg-type level & log-args]
[base-appender-args msg-type config level & log-args])}
@ -420,7 +426,7 @@
default-config?# (levels-scored s1#)
config# (if default-config?# (get-default-config) s1#)
level# (if default-config?# s1# ~s2)
compile-time-ns# ~(str *ns*)]
compile-time-ns# (get-compile-time-ns)]
;; (println "DEBUG: Runtime level check")
(when (and (level-sufficient? level# config#)
(ns-unfiltered? config# compile-time-ns#))

View File

@ -2,45 +2,74 @@
"IRC appender. Depends on https://github.com/flatland/irclj."
{:author "Emlyn Corrin"}
(:require [clojure.string :as str]
[irclj.core :as irclj]
[irclj.core :as irc]
[taoensso.timbre :as timbre]))
(def conn (atom nil))
(defn default-fmt-output-fn
[{:keys [level throwable message]}]
(format "[%s] %s%s"
(-> level name (str/upper-case))
(or message "")
(or (timbre/stacktrace throwable "\n") "")))
(defn connect [{:keys [host port pass nick user name chan]
:or {:port 6667}}]
(let [conn (irclj/connect host port nick
:username user
:real-name name
:pass pass
:callbacks {})]
(irclj/join conn chan)
(def default-appender-opts
{:async? true
:enabled? true
:min-level :info})
(defn- connect [{:keys [host port pass nick user name chan]
:or {port 6667}}]
(let [conn (irc/connect host port nick
:username user
:real-name name
:pass pass
:callbacks {})]
(irc/join conn chan)
conn))
(defn ensure-conn [conf]
(swap! conn #(or % (connect conf))))
(defn- ensure-conn [conn conf]
(if-not @conn
(reset! conn @(connect conf))))
(defn send-message [{:keys [prefix throwable message chan] :as config}]
(let [conn (ensure-conn config)
lines (-> (str message (timbre/stacktrace throwable "\n"))
(str/split #"\n"))]
(irclj/message conn chan prefix (first lines))
(doseq [line (rest lines)]
(irclj/message conn chan ">" line))))
(defn- send-message [conn chan output]
(let [[fst & rst] (str/split output #"\n")]
(irc/message conn chan fst)
(doseq [line rst]
(irc/message conn chan ">" line))))
(defn appender-fn [{:keys [ap-config prefix throwable message]}]
(when-let [irc-config (:irc ap-config)]
(send-message
(assoc irc-config
:prefix prefix
:throwable throwable
:message message))))
(defn- make-appender-fn [irc-config conn]
(fn [{:keys [ap-config] :as args}]
(when-let [irc-config (or irc-config (:irc ap-config))]
(ensure-conn conn irc-config)
(let [fmt-fn (or (:fmt-output-fn irc-config)
default-fmt-output-fn)]
(send-message conn (:chan irc-config) (fmt-fn args))))))
(def irc-appender
{:doc (str "Sends IRC messages using irclj.\n"
"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
:prefix-fn (fn [{:keys [level]}] (-> level name str/upper-case))
:fn appender-fn})
;;; Public
(defn make-irc-appender
"Sends IRC messages using irc.
Needs :irc config map in :shared-appender-config, e.g.:
{:host \"irc.example.org\" :port 6667 :nick \"logger\"
:name \"My Logger\" :chan \"#logs\"}"
[& [appender-opts {:keys [irc-config]}]]
(let [conn (atom nil)]
(merge default-appender-opts
appender-opts
{:conn conn
:doc (:doc (meta #'make-irc-appender))
:fn (make-appender-fn irc-config conn)})))
(def irc-appender "DEPRECATED: Use `make-irc-appender` instead."
(make-irc-appender))
(comment
(timbre/set-config!
[:shared-appender-config :irc]
{:host "127.0.0.1"
:nick "lazylog"
:user "lazare"
:name "Lazylus Logus"
:chan "bob"})
(timbre/set-config! [:appenders :irc] (make-irc-appender))
(timbre/log :error "A multiple\nline message\nfor you"))

View File

@ -0,0 +1,34 @@
(ns taoensso.timbre.appenders.zmq
"ØMQ appender. Requires https://github.com/zeromq/cljzmq"
{:author "Angus Fletcher"}
(:require [zeromq.zmq :as zmq]
[taoensso.timbre :as timbre]))
(defn make-zmq-socket [context transport address port]
(doto (zmq/socket context :push)
(zmq/connect (format "%s://%s:%d" transport address port))))
(defn appender-fn [socket poller {:keys [ap-config output]}]
(loop []
(zmq/poll poller 500)
(cond
(zmq/check-poller poller 0 :pollout) (zmq/send-str socket output)
(zmq/check-poller poller 0 :pollerr) (System/exit 1)
:else (recur))))
(defn make-zmq-appender
"Returns a ØMQ appender. Takes appender options and a map consisting of:
transport: a string representing transport type: tcp, ipc, inproc, pgm/epgm
address: a string containing an address to connect to.
port: a number representing the port to connect to."
[& [appender-opts {:keys [transport address port]}]]
(let [default-appender-opts {:enabled? true
:min-level :error
:async? true}
context (zmq/zcontext)
socket (make-zmq-socket context transport address port)
poller (doto (zmq/poller context)
(zmq/register socket :pollout :pollerr))]
(merge default-appender-opts
appender-opts
{:fn (partial appender-fn socket poller)})))

View File

@ -9,7 +9,7 @@
(defmacro fq-keyword "Returns namespaced keyword for given id."
[id]
`(if (and (keyword? ~id) (namespace ~id)) ~id
(keyword (str *ns*) (name ~id))))
(keyword (timbre/get-compile-time-ns) (name ~id))))
(comment (map #(fq-keyword %) ["foo" :foo :foo/bar]))
@ -56,7 +56,7 @@
(declare ^:private format-stats)
(defmacro with-pdata [level & body]
`(if-not (timbre/logging-enabled? ~level ~(str *ns*))
`(if-not (timbre/logging-enabled? ~level (timbre/get-compile-time-ns))
{:result (do ~@body)}
(binding [*pdata* (atom {})]
{:result (pspy ::clock-time ~@body)
@ -85,20 +85,6 @@
(if-not (< (rand) ~probability) (do ~@body)
(profile ~level ~id ~@body))))
(defmacro defnp "Like `defn` but wraps body in `p` macro."
{:arglists '([name ?doc-string ?attr-map [params] ?prepost-map body])}
[name & sigs]
(let [[name [params & sigs]] (encore/name-with-attrs name sigs)
prepost-map (when (and (map? (first sigs)) (next sigs)) (first sigs))
body (if prepost-map (next sigs) sigs)]
`(defn ~name ~params ~prepost-map
(pspy ~(clojure.core/name name)
~@body))))
(comment (defnp foo "Docstring "[x] "boo" (* x x))
(macroexpand '(defnp foo "Docstring "[x] "boo" (* x x)))
(profile :info :defnp-test (foo 5)))
;;;; Data capturing & aggregation
(def ^:private ^:constant stats-gc-n 111111)
@ -193,7 +179,8 @@
s-pattern (str "%" max-id-width "s %11s %9s %10s %9s %9s %7s %1s%n")
perc #(Math/round (/ %1 %2 0.01))
ft (fn [nanosecs]
(let [pow #(Math/pow 10 %)
(let [nanosecs (long nanosecs) ; Truncate any fractional nanosecs
pow #(Math/pow 10 %)
ok-pow? #(>= nanosecs (pow %))
to-pow #(encore/round (/ nanosecs (pow %1)) :round %2)]
(cond (ok-pow? 9) (str (to-pow 9 1) "s")
@ -213,19 +200,39 @@
(printf s-pattern "Accounted Time" "" "" "" "" ""
(perc accounted clock-time) (ft accounted)))))
(defmacro defnp "Like `defn` but wraps body in `p` macro."
{:arglists '([name ?doc-string ?attr-map [params] ?prepost-map body])}
[name & sigs]
(let [[name [params & sigs]] (encore/name-with-attrs name sigs)
prepost-map (when (and (map? (first sigs)) (next sigs)) (first sigs))
body (if prepost-map (next sigs) sigs)]
`(defn ~name ~params ~(or prepost-map {})
(pspy ~(clojure.core/name name)
~@body))))
;;;;
(comment (defnp foo "Docstring "[x] "boo" (* x x))
(macroexpand '(defnp foo "Docstring "[x] "boo" (* x x)))
(profile :info :defnp-test (foo 5)))
(defmacro defnp "Like `defn` but wraps fn bodies with `p` macro."
{:arglists
'([name doc-string? attr-map? [params*] prepost-map? body]
[name doc-string? attr-map? ([params*] prepost-map? body)+ attr-map?])}
[name' & sigs]
(let [[name' sigs] (encore/name-with-attrs name' sigs)
single-arity? (vector? (first sigs))
[sigs func->str]
(if single-arity?
[(list sigs) (fn [name' _params] (name name'))]
[sigs (fn [name' params] (str (name name') \_ (count params)))])
new-sigs
(map (fn [[params & others]]
(let [has-prepost-map? (and (map? (first others)) (next others))
[prepost-map & body]
(if has-prepost-map?
others
(cons {} others))]
`(~params ~prepost-map (pspy ~(func->str name' params) ~@body))))
sigs)]
`(defn ~name' ~@new-sigs)))
(comment
(defnp foo "Docstring "[x] "boo" (* x x))
(macroexpand '(defnp foo "Docstring" [x] "boo" (* x x)))
(macroexpand '(defnp foo "Docstring" ([x] (* x x))
([x y] (* x y))))
(profile :info :defnp-test (foo 5)))
;;;;
(comment
(profile :info :sleepy-threads