2013-11-22 21:02:33 +00:00
**[API docs](http://ptaoussanis.github.io/timbre/)** | ** [CHANGELOG ](https://github.com/ptaoussanis/timbre/blob/master/CHANGELOG.md )** | [contact & contributing ](#contact--contribution ) | [other Clojure libs ](https://www.taoensso.com/clojure-libraries ) | [Twitter ](https://twitter.com/#!/ptaoussanis ) | current [semantic ](http://semver.org/ ) version:
2012-06-29 07:13:28 +00:00
```clojure
2013-11-30 13:19:56 +00:00
[com.taoensso/timbre "3.0.0-RC1"] ; Non-breaking upgrade - see CHANGELOG for details
2012-06-29 07:13:28 +00:00
```
2013-11-30 13:19:56 +00:00
Appender authors: please see [here ](https://github.com/ptaoussanis/timbre/issues/41 ) about migrating Timbre 2.x appenders to 3.x's recommended style.
2012-11-04 16:43:55 +00:00
# Timbre, a (sane) Clojure logging & profiling library
2012-07-13 10:37:40 +00:00
2013-11-30 13:19:56 +00:00
Logging with Java can be maddeningly, unnecessarily hard. Particularly if all you want is something *simple that works out-the-box* . Timbre brings functional, Clojure-y goodness to all your logging needs. **No XML!**
2012-05-28 08:13:11 +00:00
2013-06-01 12:41:07 +00:00
## What's in the box™?
2012-05-28 08:13:11 +00:00
* Small, uncomplicated **all-Clojure** library.
2012-07-03 11:48:00 +00:00
* **Super-simple map-based config**: no arcane XML or properties files!
2013-08-20 16:09:30 +00:00
* **Low overhead** with dynamic logging level.
* **No overhead** with compile-time logging level. (v2.6+)
2013-02-04 05:32:32 +00:00
* Flexible **fn-centric appender model** with **middleware** .
2012-05-28 08:13:11 +00:00
* Sensible built-in appenders including simple **email appender** .
2013-05-15 15:37:02 +00:00
* Tunable **rate limit** and **asynchronous** logging support.
2012-07-03 11:48:00 +00:00
* Robust **namespace filtering** .
2013-11-30 13:19:56 +00:00
* [tools.logging ](https://github.com/clojure/tools.logging ) support (optional, useful when integrating with legacy logging systems).
2012-07-03 11:48:00 +00:00
* Dead-simple, logging-level-aware **logging profiler** .
2012-05-28 08:13:11 +00:00
2013-06-01 12:41:07 +00:00
## Getting started
2012-05-28 08:13:11 +00:00
2013-06-01 12:41:07 +00:00
### Dependencies
2012-05-28 08:13:11 +00:00
2013-11-30 10:03:04 +00:00
Add the necessary dependency to your [Leiningen ](http://leiningen.org/ ) `project.clj` and use the supplied ns-import helper:
2012-06-29 07:13:28 +00:00
```clojure
2013-11-30 13:19:56 +00:00
[com.taoensso/timbre "3.0.0-RC1"] ; project.clj
2013-11-30 10:03:04 +00:00
(ns my-app (:require [taoensso.timbre :as timbre])) ; Your ns
(timbre/refer-timbre) ; Provides useful Timbre aliases in this ns
```
2013-11-30 13:19:56 +00:00
The `refer-timbre` call is a convenience fn that executes:
2013-11-30 10:03:04 +00:00
```clojure
(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)])
(require '[taoensso.timbre.profiling :as profiling :refer (pspy profile defnp)])
2012-05-28 08:13:11 +00:00
```
2012-11-04 16:43:55 +00:00
### Logging
2012-05-28 08:13:11 +00:00
2012-05-28 10:49:54 +00:00
By default, Timbre gives you basic print output to `*out*` /`*err*` at a `debug` logging level:
```clojure
2013-11-30 13:19:56 +00:00
(info "This will print") => nil
2012-07-26 10:15:31 +00:00
%> 2012-May-28 17:26:11:444 +0700 localhost INFO [my-app] - This will print
2012-07-03 11:48:00 +00:00
2013-11-30 13:19:56 +00:00
(spy :info (* 5 4 3 2 1)) => 120
2012-07-26 10:15:31 +00:00
%> 2012-May-28 17:26:14:138 +0700 localhost INFO [my-app] - (* 5 4 3 2 1) 120
2012-05-28 10:49:54 +00:00
2013-11-30 13:19:56 +00:00
(trace "This won't print due to insufficient logging level") => nil
2012-05-28 10:49:54 +00:00
```
2013-11-30 13:19:56 +00:00
First-argument exceptions generate a nicely cleaned-up stack trace using [io.aviso.exception ](https://github.com/AvisoNovate/pretty ).
2012-05-28 10:49:54 +00:00
```clojure
(info (Exception. "Oh noes") "arg1" "arg2")
2012-07-26 10:15:31 +00:00
%> 2012-May-28 17:35:16:132 +0700 localhost INFO [my-app] - arg1 arg2
2012-05-28 10:49:54 +00:00
java.lang.Exception: Oh noes
2012-07-03 11:48:00 +00:00
NO_SOURCE_FILE:1 my-app/eval6409
2012-05-28 10:49:54 +00:00
Compiler.java:6511 clojure.lang.Compiler.eval
2012-07-03 11:48:00 +00:00
< ... >
2012-05-28 10:49:54 +00:00
```
2012-05-28 08:13:11 +00:00
### Configuration
2013-11-30 13:19:56 +00:00
This is the biggest win over Java logging utilities IMO. Here's `timbre/example-config` (also Timbre's default config):
```clojure
(def example-config
"APPENDERS
An appender is a map with keys:
:doc ; (Optional) string.
:min-level ; (Optional) keyword, or nil (no minimum level).
:enabled? ; (Optional).
:async? ; (Optional) dispatch using agent (good for slow appenders).
: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.
An appender's fn takes a single map with keys:
:level ; Keyword.
:error? ; Is level an 'error' level?.
:throwable ; java.lang.Throwable.
:args ; Raw logging macro args (as given to `info` , etc.).
:message ; Stringified logging macro args, or nil.
:output ; Output of `fmt-output-fn` , used by built-in appenders
; as final, formatted appender output. Appenders may (but
; are not obligated to) use this as their output.
2013-12-01 12:59:09 +00:00
:ap-config ; Contents of config's :shared-appender-config key.
2013-11-30 13:19:56 +00:00
:profile-stats ; From `profile` macro.
:instant ; java.util.Date.
:timestamp ; String generated from :timestamp-pattern, :timestamp-locale.
:hostname ; String.
:ns ; String.
MIDDLEWARE
Middleware are fns (applied right-to-left) that transform the map
dispatched to appender fns. If any middleware returns nil, no dispatching
will occur (i.e. the event will be filtered).
The `example-config` code contains further settings and details.
See also `set-config!` , `merge-config!` , `set-level!` ."
{;;; Control log filtering by namespace patterns (e.g. ["my-app.*"]).
;;; Useful for turning off logging in noisy libraries, etc.
:ns-whitelist []
:ns-blacklist []
;; Fns (applied right-to-left) to transform/filter appender fn args.
;; Useful for obfuscating credentials, pattern filtering, etc.
:middleware []
;;; Control :timestamp format
:timestamp-pattern "yyyy-MMM-dd HH:mm:ss ZZ" ; SimpleDateFormat pattern
:timestamp-locale nil ; A Locale object, or nil
;; Output formatter used by built-in appenders. Custom appenders may (but are
;; not required to use) its output (:output). Extra per-appender opts can be
;; supplied as an optional second (map) arg.
:fmt-output-fn
(fn [{:keys [level throwable message timestamp hostname ns]}
;; Any extra appender-specific opts:
& [{:keys [nofonts?] :as appender-fmt-output-opts}]]
;; < timestamp > < hostname > < LEVEL > [< ns > ] - < message > < throwable >
(format "%s %s %s [%s] - %s%s"
timestamp hostname (-> level name str/upper-case) ns (or message "")
(or (stacktrace throwable "\n" (when nofonts? {})) "")))
:shared-appender-config {} ; Provided to all appenders via :ap-config key
:appenders
{:standard-out
{:doc "Prints to *out* /*err*. Enabled by default."
:min-level nil :enabled? true :async? false :rate-limit nil
2013-12-01 12:59:09 +00:00
:fn (fn [{:keys [error? output]}] ; Use any appender args
2013-11-30 13:19:56 +00:00
(binding [*out* (if error? *err* *out* )]
(str-println output)))}
:spit
{:doc "Spits to `(:spit-filename :shared-appender-config)` file."
:min-level nil :enabled? false :async? false :rate-limit nil
2013-12-01 12:59:09 +00:00
:fn (fn [{:keys [ap-config output]}] ; Use any appender args
2013-11-30 13:19:56 +00:00
(when-let [filename (:spit-filename ap-config)]
(try (spit filename output :append true)
(catch java.io.IOException _))))}}})
2012-07-03 09:30:50 +00:00
```
2013-11-30 13:19:56 +00:00
A few things to note:
2012-05-28 10:49:54 +00:00
2013-11-30 13:19:56 +00:00
* Appenders are trivial to write & configure - **they're just fns** . It's Timbre's job to dispatch useful args to appenders when appropriate, it's their job to do something interesting with them.
* Being 'just fns', appenders have basically limitless potential: write to your database, send a message over the network, check some other state (e.g. environment config) before making a choice, etc.
2012-05-28 10:49:54 +00:00
2013-11-30 13:19:56 +00:00
The **logging level** may be set:
* At compile-time: (`TIMBRE_LOG_LEVEL` environment variable).
* Via an atom: `(timbre/set-level! <level>)` . (Usual method).
* Via dynamic thread-level binding: `(timbre/with-log-level <level> ...)` .
2012-05-30 09:15:15 +00:00
2013-11-30 13:19:56 +00:00
A compile-time level offers _zero-overhead_ performance since it'll cause insufficient logging calls to disappear completely at compile-time. Usually you won't need/want to bother: Timbre offers very decent performance with runtime level checks (~15msecs/10k checks on my Macbook Air).
2012-05-30 09:15:15 +00:00
2013-11-30 13:19:56 +00:00
For common-case ease-of-use, **all logging utils use a global atom for their config** . This is configurable with `timbre/set-config!` , `timbre/merge-config!` . The lower-level `log` and `logf` macros also take an optional first-arg config map for greater flexibility (e.g. **during testing** ).
2013-11-30 10:03:04 +00:00
2013-06-01 12:41:07 +00:00
### Built-in appenders
2012-07-13 10:07:23 +00:00
2013-06-01 12:41:07 +00:00
#### File appender
2012-07-13 10:07:23 +00:00
```clojure
2013-05-15 09:41:41 +00:00
(timbre/set-config! [:appenders :spit :enabled?] true)
(timbre/set-config! [:shared-appender-config :spit-filename] "/path/my-file.log")
2012-07-13 10:07:23 +00:00
```
2013-06-01 12:41:07 +00:00
#### Email ([Postal](https://github.com/drewr/postal)) appender
2012-05-28 10:49:54 +00:00
```clojure
2013-11-30 13:19:56 +00:00
;; [com.draines/postal "1.9.2"] ; Add to project.clj deps
2013-05-15 09:41:41 +00:00
;; (:require [taoensso.timbre.appenders (postal :as postal-appender)]) ; Add to ns
2012-07-13 10:07:23 +00:00
2013-11-29 06:39:09 +00:00
(timbre/set-config! [:appenders :postal]
(postal-appender/make-postal-appender
{:enabled? true
:rate-limit [1 60000] ; 1 msg / 60,000 msecs (1 min)
:async? true ; Don't block waiting for email to send
}
{:postal-config
^{:host "mail.isp.net" :user "jsmith" :pass "sekrat!!1"}
{:from "me@draines.com" :to "foo@example.com"}}))
2012-05-28 10:49:54 +00:00
```
2013-11-30 13:19:56 +00:00
#### Redis ([Carmine](https://github.com/ptaoussanis/carmine)) appender
2013-04-19 13:28:02 +00:00
2013-11-30 13:19:56 +00:00
```clojure
;; [com.taoensso/carmine "2.4.0"] ; Add to project.clj deps
;; (:require [taoensso.timbre.appenders (:carmine :as car-appender)]) ; Add to ns
2013-06-06 11:40:50 +00:00
2013-11-30 13:19:56 +00:00
(timbre/set-config! [:appenders :carmine] (postal-appenders/make-carmine-appender))
```
2013-06-05 12:52:23 +00:00
2013-11-30 13:19:56 +00:00
This gives us an 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.
2012-05-28 10:49:54 +00:00
2013-11-30 13:19:56 +00:00
Entries are serialized with their raw logging arguments and so well suited for Clojure's usual seq tools (`filter`, `map` , `group-by` , etc.). Even exceptions are maintained and queryable later. A simple query utility is provided: `car-appender/query-entries` .
2012-05-28 10:49:54 +00:00
2013-11-30 13:19:56 +00:00
#### Other included appenders
2012-05-28 10:49:54 +00:00
2013-11-30 13:19:56 +00:00
A number of 3rd-party appenders are included out-the-box for: Android, IRC, sockets, MongoDB, and rotating files. These are all located in the `taoensso.timbre.appenders.x` namespaces - **please see the relevant docstrings for details** .
2012-05-30 06:47:53 +00:00
2013-11-30 13:19:56 +00:00
Thanks to their respective authors! Just give me a shout if you've got an appender you'd like to have added.
2012-07-03 11:48:00 +00:00
## Profiling
The usual recommendation for Clojure profiling is: use a good **JVM profiler** like [YourKit ](http://www.yourkit.com/ ), [JProfiler ](http://www.ej-technologies.com/products/jprofiler/overview.html ), or [VisualVM ](http://docs.oracle.com/javase/6/docs/technotes/guides/visualvm/index.html ).
2013-06-06 11:40:50 +00:00
And these certainly do the job. But as with many Java tools, they can be a little hairy and often heavy-handed - especially when applied to Clojure. Timbre includes an alternative.
2012-07-03 11:48:00 +00:00
Wrap forms that you'd like to profile with the `p` macro and give them a name:
```clojure
(defn my-fn
[]
(let [nums (vec (range 1000))]
2012-07-03 13:50:06 +00:00
(+ (p :fast-sleep (Thread/sleep 1) 10)
(p :slow-sleep (Thread/sleep 2) 32)
2012-07-03 11:48:00 +00:00
(p :add (reduce + nums))
(p :sub (reduce - nums))
(p :mult (reduce * nums))
(p :div (reduce / nums)))))
2013-11-30 13:19:56 +00:00
(my-fn) => 42
2012-07-03 11:48:00 +00:00
```
The `profile` macro can now be used to log times for any wrapped forms:
```clojure
2013-11-30 13:19:56 +00:00
(profile :info :Arithmetic (dotimes [n 100] (my-fn))) => "Done!"
2012-07-26 10:15:31 +00:00
%> 2012-Jul-03 20:46:17 +0700 localhost INFO [my-app] - Profiling my-app/Arithmetic
2012-07-04 06:47:21 +00:00
Name Calls Min Max MAD Mean Total% Total
my-app/slow-sleep 100 2ms 2ms 31μs 2ms 57 231ms
my-app/fast-sleep 100 1ms 1ms 27μs 1ms 29 118ms
my-app/add 100 44μs 2ms 46μs 100μs 2 10ms
my-app/sub 100 42μs 564μs 26μs 72μs 2 7ms
my-app/div 100 54μs 191μs 17μs 71μs 2 7ms
my-app/mult 100 31μs 165μs 11μs 44μs 1 4ms
Unaccounted 6 26ms
Total 100 405ms
2012-07-03 11:48:00 +00:00
```
2013-11-30 10:03:04 +00:00
You can also use the `defnp` macro to conveniently wrap whole fns.
2012-07-04 06:47:21 +00:00
It's important to note that Timbre profiling is fully **logging-level aware** : if the level is insufficient, you *won't pay for profiling* . Likewise, normal namespace filtering applies. (Performance characteristics for both checks are inherited from Timbre itself).
2012-07-03 11:48:00 +00:00
2012-07-03 13:50:06 +00:00
And since `p` and `profile` **always return their body's result** regardless of whether profiling actually happens or not, it becomes feasible to use profiling more often as part of your normal workflow: just *leave profiling code in production as you do for logging code* .
2012-07-03 11:48:00 +00:00
2012-07-04 06:47:21 +00:00
A simple **sampling profiler** is also available: `taoensso.timbre.profiling/sampling-profile` .
2012-05-28 08:13:11 +00:00
2013-07-09 07:48:48 +00:00
## This project supports the CDS and ![ClojureWerkz](https://raw.github.com/clojurewerkz/clojurewerkz.org/master/assets/images/logos/clojurewerkz_long_h_50.png) goals
2013-06-01 12:41:07 +00:00
2013-06-22 10:43:56 +00:00
* [CDS ](http://clojure-doc.org/ ), the **Clojure Documentation Site** , is a **contributer-friendly** community project aimed at producing top-notch, **beginner-friendly** Clojure tutorials and documentation. Awesome resource.
2013-06-01 12:41:07 +00:00
2013-06-22 10:43:56 +00:00
* [ClojureWerkz ](http://clojurewerkz.org/ ) is a growing collection of open-source, **batteries-included Clojure libraries** that emphasise modern targets, great documentation, and thorough testing. They've got a ton of great stuff, check 'em out!
2012-06-16 06:53:38 +00:00
2013-06-01 12:41:07 +00:00
## Contact & contribution
2012-11-05 17:48:42 +00:00
2013-06-01 12:41:07 +00:00
Please use the [project's GitHub issues page ](https://github.com/ptaoussanis/timbre/issues ) for project questions/comments/suggestions/whatever ** (pull requests welcome!)**. Am very open to ideas if you have any!
2012-05-28 08:13:11 +00:00
2013-06-01 12:41:07 +00:00
Otherwise reach me (Peter Taoussanis) at [taoensso.com ](https://www.taoensso.com ) or on Twitter ([@ptaoussanis](https://twitter.com/#!/ptaoussanis)). Cheers!
2012-05-28 08:13:11 +00:00
## License
2013-11-30 10:03:04 +00:00
Copyright © 2012, 2013 Peter Taoussanis. Distributed under the [Eclipse Public License ](http://www.eclipse.org/legal/epl-v10.html ), the same as Clojure.