2012-07-05 09:48:27 +00:00
Current [semantic ](http://semver.org/ ) version:
2012-06-29 07:13:28 +00:00
```clojure
2012-07-05 09:48:27 +00:00
[com.taoensso/timbre "0.6.1"] ; Please note that the repo and ns have changed recently
2012-06-29 07:13:28 +00:00
```
2012-05-28 11:22:23 +00:00
# Timbre, a (sane) logging library for Clojure
2012-05-28 06:22:02 +00:00
2012-05-28 08:13:11 +00:00
Logging with Java can be maddeningly, unnecessarily hard. Particularly if all you want is something *simple that works out the box* .
[tools.logging ](https://github.com/clojure/tools.logging ) helps, but it doesn't save you from the mess of logger dependencies and configuration hell.
Timbre is an attempt to make **simple logging simple** and more **complex logging possible** .
## What's In The Box?
* Small, uncomplicated **all-Clojure** library.
2012-07-03 11:48:00 +00:00
* **Super-simple map-based config**: no arcane XML or properties files!
2012-05-28 08:13:11 +00:00
* Decent performance (**low overhead**).
* Flexible **fn-centric appender model** .
* Sensible built-in appenders including simple **email appender** .
2012-07-03 11:48:00 +00:00
* Tunable **flood control** and **asynchronous** logging support.
* Robust **namespace filtering** .
* Dead-simple, logging-level-aware **logging profiler** .
2012-05-28 08:13:11 +00:00
2012-07-03 09:30:50 +00:00
## Status [![Build Status](https://secure.travis-ci.org/ptaoussanis/timbre.png?branch=master)](http://travis-ci.org/ptaoussanis/timbre)
2012-05-28 08:13:11 +00:00
2012-07-07 13:19:10 +00:00
Timbre is still currently *experimental* . It **has not yet been thoroughly tested in production** and its API is subject to change. To run tests against all supported Clojure versions, use:
2012-05-28 08:13:11 +00:00
2012-07-03 09:30:50 +00:00
```bash
lein2 all test
```
2012-05-28 08:13:11 +00:00
## Getting Started
### Leiningen
2012-06-29 07:13:28 +00:00
Depend on Timbre in your `project.clj` :
```clojure
2012-07-05 09:48:27 +00:00
[com.taoensso/timbre "0.6.1"]
2012-06-29 07:13:28 +00:00
```
2012-07-03 09:30:50 +00:00
and `use` the library:
2012-05-28 08:13:11 +00:00
```clojure
(ns my-app
2012-07-03 11:48:00 +00:00
(:use [taoensso.timbre :as timbre :only (trace debug info warn error fatal spy)]))
2012-05-28 08:13:11 +00:00
```
### Start Logging
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
2012-05-28 10:57:23 +00:00
(info "This will print")
2012-07-03 11:48:00 +00:00
=> nil
%> 2012-May-28 17:26:11:444 +0700 INFO [my-app] - This will print
(spy :info (* 5 4 3 2 1))
=> 120
%> 2012-May-28 17:26:14:138 +0700 INFO [my-app] - (* 5 4 3 2 1) 120
2012-05-28 10:49:54 +00:00
(trace "This won't print due to insufficient logging level")
=> nil
```
There's little overhead for checking logging levels:
```clojure
(time (trace (Thread/sleep 5000)))
2012-07-03 11:48:00 +00:00
%> "Elapsed time: 0.054 msecs"
2012-05-28 10:49:54 +00:00
(time (when true))
2012-07-03 11:48:00 +00:00
%> "Elapsed time: 0.051 msecs"
2012-05-28 10:49:54 +00:00
```
2012-05-28 10:57:23 +00:00
First-argument exceptions generate a stack trace:
2012-05-28 10:49:54 +00:00
```clojure
(info (Exception. "Oh noes") "arg1" "arg2")
2012-07-03 11:48:00 +00:00
%> 2012-May-28 17:35:16:132 +0700 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
2012-07-03 09:30:50 +00:00
Configuring Timbre couldn't be simpler. Let's check out (some of) the defaults:
```clojure
@timbre/config
=>
{:current-level :debug
:ns-whitelist []
:ns-blacklist []
:appenders
2012-07-03 11:48:00 +00:00
{:standard-out { < ... > }
:postal { < ... > }}
2012-07-03 09:30:50 +00:00
:shared-appender-config
{:timestamp-pattern "yyyy-MMM-dd HH:mm:ss ZZ"
:locale nil
:postal nil}}
```
2012-05-28 10:49:54 +00:00
Easily adjust the current logging level:
```clojure
2012-05-28 13:17:38 +00:00
(timbre/set-level! :warn)
2012-05-28 10:49:54 +00:00
```
2012-05-30 13:07:34 +00:00
And the default timestamp formatting for log messages:
2012-05-30 09:15:15 +00:00
```clojure
2012-05-30 13:07:34 +00:00
(timbre/set-config! [:shared-appender-config :timestamp-pattern]
2012-05-30 09:15:15 +00:00
"yyyy-MMM-dd HH:mm:ss ZZ")
(timbre/set-config! [:shared-appender-config :locale]
(java.util.Locale/GERMAN))
```
2012-07-03 09:30:50 +00:00
Filter logging output by namespaces:
```clojure
(timbre/set-config! [:ns-whitelist] ["some.library.core" "my-app.*"])
```
2012-05-28 10:57:23 +00:00
Enable the standard [Postal ](https://github.com/drewr/postal )-based email appender:
2012-05-28 10:49:54 +00:00
```clojure
2012-05-28 13:17:38 +00:00
(timbre/set-config! [:shared-appender-config :postal]
^{: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
2012-05-28 13:17:38 +00:00
(timbre/set-config! [:appenders :postal :enabled?] true)
2012-05-28 10:49:54 +00:00
```
2012-05-28 11:22:23 +00:00
Rate-limit to one email per message per minute:
2012-05-28 10:49:54 +00:00
```clojure
2012-05-28 13:25:25 +00:00
(timbre/set-config! [:appenders :postal :max-message-per-msecs] 60000)
2012-05-28 10:49:54 +00:00
```
And make sure emails are sent asynchronously:
```clojure
2012-05-28 13:17:38 +00:00
(timbre/set-config! [:appenders :postal :async?] true)
2012-05-28 10:49:54 +00:00
```
### Custom Appenders
2012-07-03 09:30:50 +00:00
Writing a custom appender is dead-easy:
2012-05-28 10:49:54 +00:00
```clojure
2012-05-28 13:17:38 +00:00
(timbre/set-config!
[:appenders :my-appender]
{:doc "Hello-world appender"
:min-level :debug
:enabled? true
:async? false
:max-message-per-msecs nil ; No rate limiting
2012-05-30 17:05:36 +00:00
:fn (fn [{:keys [ap-config level error? instant timestamp
2012-05-30 09:15:15 +00:00
ns message more] :as args}]
2012-07-03 11:48:00 +00:00
(when-not (:my-production-mode? ap-config)
2012-05-30 17:05:36 +00:00
(apply println timestamp "Hello world!" message more)))
2012-05-28 10:49:54 +00:00
```
2012-05-30 06:47:53 +00:00
And because appender fns are just regular Clojure fns, you have *unlimited power* : write to your database, send a message over the network, check some other state (e.g. environment config) before making a choice, etc.
2012-07-03 11:48:00 +00:00
See the `timbre/config` docstring for more information on appenders.
## 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 ).
And these certaily 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.
Let's add it to our app's `ns` declaration:
```clojure
(ns my-app
(:use [taoensso.timbre :as timbre :only (trace debug info warn error fatal spy)]
[taoensso.timbre.profiling :as profiling :only (p profile)]))
```
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)))))
(my-fn)
=> 42
```
The `profile` macro can now be used to log times for any wrapped forms:
```clojure
(profile :info :Arithmetic (dotimes [n 100] (my-fn)))
=> "Done!"
2012-07-04 06:47:21 +00:00
%> 2012-Jul-03 20:46:17 +0700 INFO [my-app] - Profiling my-app/Arithmetic
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
```
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
2012-07-06 17:31:26 +00:00
## Timbre supports the ClojureWerkz Project Goals
2012-06-16 06:53:38 +00:00
2012-06-20 10:53:06 +00:00
ClojureWerkz is a growing collection of open-source, batteries-included [Clojure libraries ](http://clojurewerkz.org/ ) that emphasise modern targets, great documentation, and thorough testing.
2012-06-16 06:53:38 +00:00
2012-05-28 08:13:11 +00:00
## Contact & Contribution
2012-05-30 05:52:09 +00:00
Reach me (Peter Taoussanis) at *ptaoussanis at gmail.com* for questions/comments/suggestions/whatever. I'm very open to ideas if you have any!
2012-05-28 10:57:23 +00:00
I'm also on Twitter: [@ptaoussanis ](https://twitter.com/#!/ptaoussanis ).
2012-05-28 08:13:11 +00:00
## License
2012-06-12 15:02:24 +00:00
Copyright © 2012 Peter Taoussanis
2012-07-03 11:48:00 +00:00
Distributed under the [Eclipse Public License ](http://www.eclipse.org/legal/epl-v10.html ), the same as Clojure.