2016-01-14 05:17:22 +00:00
< a href = "https://www.taoensso.com" title = "More stuff by @ptaoussanis at www.taoensso.com" >
2016-01-14 07:18:33 +00:00
< img src = "https://www.taoensso.com/taoensso-open-source.png" alt = "Taoensso open-source" width = "400" / > < / a >
2016-01-14 05:17:22 +00:00
**[CHANGELOG]** | [API] | current [Break Version]:
2012-06-29 07:13:28 +00:00
```clojure
2016-01-14 05:17:22 +00:00
[com.taoensso/timbre "4.2.1"] ; Stable
2012-06-29 07:13:28 +00:00
```
2016-01-14 05:17:22 +00:00
# Timbre
## A pure Clojure/Script logging library
2012-07-13 10:37:40 +00:00
2015-07-17 15:13:11 +00:00
Java logging is a mess of complexity that buys you _nothing_ . It can be comically hard to get even the simplest logging working, and it's no better at scale.
2015-05-26 05:43:22 +00:00
2015-07-17 15:13:11 +00:00
Timbre offers an **all Clojure/Script** alternative that **works out the box** . It's fast, deeply flexible, and easy to configure. **No XML!**
2012-05-28 08:13:11 +00:00
2016-01-14 05:17:22 +00:00
## Features
* Full **Clojure** + **ClojureScript** support (v4+)
* No XML or properties files. **A single, simple config map** , and you're set
* Deeply flexible **fn appender model** with **middleware**
* **Great performance** at any scale
* Filter logging by levels and **namespace whitelist/blacklist patterns**
* **Zero overhead** with **complete Clj+Cljs elision** for compile-time level/ns filters
* Useful built-in appenders for **out-the-box** Clj+Cljs logging
* Powerful, easy-to-configure per-appender **rate limits** and **async logging**
* [Logs as Clojure values] (v3+)
* [tools.logging] support (optional, useful when integrating with legacy logging systems)
* Level and ns-filter aware **logging profiler**
* Small, simple, cross-platform codebase
2012-05-28 08:13:11 +00:00
2014-04-03 10:06:45 +00:00
## 3rd-party tools, appenders, etc.
2015-12-22 06:17:13 +00:00
Link | Description
------------------------ | -----------------------------------------------------
[@palletops/log-config] | Library to help manage Timbre logging config
[@fzakaria/slf4j-timbre] | Route log4j/logback/sfl4j log output to Timbre
Your link here? | **PR's welcome!**
2014-04-03 10:06:45 +00:00
2013-06-01 12:41:07 +00:00
## Getting started
2012-05-28 08:13:11 +00:00
2016-01-14 05:17:22 +00:00
Add the necessary dependency to your project:
```clojure
[com.taoensso/timbre "4.2.1"]
```
2012-05-28 08:13:11 +00:00
2016-01-14 05:17:22 +00:00
And setup your namespace imports:
2012-06-29 07:13:28 +00:00
```clojure
2013-11-30 10:03:04 +00:00
2016-01-14 05:17:22 +00:00
(ns my-clj-ns ; Clojure namespace
2015-05-29 02:18:07 +00:00
(:require
[taoensso.timbre :as timbre
:refer (log trace debug info warn error fatal report
2015-06-13 12:25:30 +00:00
logf tracef debugf infof warnf errorf fatalf reportf
spy get-env log-env)]
2015-05-29 02:18:07 +00:00
[taoensso.timbre.profiling :as profiling
:refer (pspy pspy* profile defnp p p*)]))
2016-01-14 05:17:22 +00:00
(ns my-cljs-ns ; ; ClojureScript namespace
2015-05-29 02:18:07 +00:00
(:require
[taoensso.timbre :as timbre
:refer-macros (log trace debug info warn error fatal report
2015-06-13 12:25:30 +00:00
logf tracef debugf infof warnf errorf fatalf reportf
spy get-env log-env)]))
2012-05-28 08:13:11 +00:00
```
2016-01-14 05:17:22 +00:00
> You can also call `(timbre/refer-timbre)` to configure Clj ns referrals **automatically**
2015-05-26 05:43:22 +00:00
2012-11-04 16:43:55 +00:00
### Logging
2012-05-28 08:13:11 +00:00
2015-05-29 05:37:52 +00:00
By default, Timbre gives you basic `println` and `js/console` (v4+) output at a `:debug` log level:
2012-05-28 10:49:54 +00:00
```clojure
2013-11-30 13:19:56 +00:00
(info "This will print") => nil
2015-06-13 12:25:30 +00:00
%> 15-Jun-13 19:18:33 localhost INFO [my-app.core] - 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
2015-06-13 12:25:30 +00:00
%> 15-Jun-13 19:19:13 localhost INFO [my-app.core] - (* 5 4 3 2 1) => 120
(defn my-mult [x y] (info "Lexical env:" (get-env)) (* x y)) => #'my-mult
(my-mult 4 7) => 28
%> 15-Jun-13 19:21:53 localhost INFO [my-app.core] - Lexical env: {x 4, y 7}
2012-05-28 10:49:54 +00:00
2015-05-26 14:43:28 +00:00
(trace "This won't print due to insufficient log level") => nil
2012-05-28 10:49:54 +00:00
```
2016-01-14 05:17:22 +00:00
First-argument exceptions generate a nicely cleaned-up stack trace using [io.aviso.exception] (Clj only):
2012-05-28 10:49:54 +00:00
```clojure
(info (Exception. "Oh noes") "arg1" "arg2")
2015-06-13 12:25:30 +00:00
%> 15-Jun-13 19:22:55 localhost INFO [my-app.core] - arg1 arg2
java.lang.Exception: On noes
< Stacktrace >
2012-05-28 10:49:54 +00:00
```
2012-05-28 08:13:11 +00:00
2016-01-14 05:17:22 +00:00
Other utils include: `log-errors` , `log-and-rethrow-errors` , `logged-future` , and `handle-uncaught-jvm-exceptions!` (please see the [API] for details).
2015-09-28 07:08:41 +00:00
2015-09-28 12:03:11 +00:00
#### Disabling stacktrace colors
ANSI colors are enabled by default for stacktraces. To turn these off (e.g. for log files or emails), you can add the following entry to your top-level config **or** individual appender map/s:
2015-09-28 07:08:41 +00:00
```clojure
:output-fn (partial timbre/default-output-fn {:stacktrace-fonts {}})
```
2012-05-28 08:13:11 +00:00
### Configuration
2015-05-26 05:43:22 +00:00
This is the biggest win over Java logging IMO. Here's `timbre/example-config` (also Timbre's default config):
2015-05-26 14:43:28 +00:00
> The example below shows config for **Timbre v4**. See [here](https://github.com/ptaoussanis/timbre/tree/v3.4.0#configuration) for an example of **Timbre v3** config.
2013-11-30 13:19:56 +00:00
```clojure
(def example-config
2015-05-26 05:43:22 +00:00
"Example (+default) Timbre v4 config map.
APPENDERS
An appender is a map with keys:
:min-level ; Level keyword, or nil (=> no minimum level)
:enabled? ;
:async? ; Dispatch using agent? Useful for slow appenders
:rate-limit ; [[ncalls-limit window-ms] < ... > ], or nil
2015-05-27 07:10:01 +00:00
:output-fn ; Optional override for inherited (fn [data]) -> string
:fn ; (fn [data]) -> side effects, with keys described below
2015-05-26 05:43:22 +00:00
An appender's fn takes a single data map with keys:
:config ; Entire config map (this map, etc.)
2015-05-26 14:43:28 +00:00
:appender-id ; Id of appender currently dispatching
:appender ; Entire map of appender currently dispatching
2015-05-26 05:43:22 +00:00
:instant ; Platform date (java.util.Date or js/Date)
:level ; Keyword
2015-05-26 14:43:28 +00:00
:error-level? ; Is level e/o #{:error :fatal}?
2015-05-26 05:43:22 +00:00
:?ns-str ; String, or nil
:?file ; String, or nil ; Waiting on CLJ-865
:?line ; Integer, or nil ; Waiting on CLJ-865
2015-05-26 14:43:28 +00:00
:?err_ ; Delay - first-arg platform error, or nil
2015-05-26 05:43:22 +00:00
:vargs_ ; Delay - raw args vector
:hostname_ ; Delay - string (clj only)
:msg_ ; Delay - args string
:timestamp_ ; Delay - string
2015-05-27 07:10:01 +00:00
:output-fn ; (fn [data]) -> formatted output string
2015-07-20 11:40:20 +00:00
; (see `default-output-fn` for details)
2015-05-26 05:43:22 +00:00
2015-07-17 12:01:01 +00:00
:context ; *context* value at log time (see `with-context` )
2015-05-26 05:43:22 +00:00
:profile-stats ; From `profile` macro
MIDDLEWARE
Middleware are simple (fn [data]) -> ?data fns (applied left->right) that
transform the data map dispatched to appender fns. If any middleware returns
nil, NO dispatching will occur (i.e. the event will be filtered).
The `example-config` source code contains further settings and details.
2013-11-30 13:19:56 +00:00
See also `set-config!` , `merge-config!` , `set-level!` ."
2015-05-26 07:28:07 +00:00
{:level :debug ; e/o #{:trace :debug :info :warn :error :fatal :report}
;; Control log filtering by namespaces/patterns. Useful for turning off
;; logging in noisy libraries, etc.:
2015-05-26 15:40:56 +00:00
:ns-whitelist [] #_ ["my-app.foo-ns"]
:ns-blacklist [] #_ ["taoensso.*"]
2015-05-26 07:28:07 +00:00
:middleware [] ; (fns [data]) -> ?data, applied left->right
2015-05-27 07:10:01 +00:00
;; Clj only:
:timestamp-opts default-timestamp-opts ; {:pattern _ :locale _ :timezone _}
:output-fn default-output-fn ; (fn [data]) -> string
2015-05-26 07:28:07 +00:00
:appenders
2015-05-27 07:10:01 +00:00
{:example-println-appender ; Appender id
2015-05-26 07:28:07 +00:00
;; Appender definition (just a map):
2015-05-27 07:10:01 +00:00
{:enabled? true
:async? false
:min-level nil
2015-05-26 07:28:07 +00:00
:rate-limit [[1 250] [10 5000]] ; 1/250ms, 10/5s
2015-05-27 07:10:01 +00:00
:output-fn :inherit
2015-05-26 07:28:07 +00:00
:fn ; Appender's fn
(fn [data]
2015-05-26 11:15:28 +00:00
(let [{:keys [output-fn]} data
formatted-output-str (output-fn data)]
(println formatted-output-str)))}}})
2012-07-03 09:30:50 +00:00
```
2013-11-30 13:19:56 +00:00
A few things to note:
2016-01-14 05:17:22 +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
2015-05-29 05:37:52 +00:00
#### Log levels and ns filters
2015-05-26 14:43:28 +00:00
The **log level** may be set:
2016-01-14 05:17:22 +00:00
* At compile-time: (`TIMBRE_LEVEL` environment variable)
* Statically using: `timbre/set-level!` /`timbre/merge-level!`
* Dynamically using: `timbre/with-level`
2015-05-26 11:15:28 +00:00
2015-05-29 05:37:52 +00:00
The **ns filters** may be set:
2016-01-14 05:17:22 +00:00
* At compile-time: (`TIMBRE_NS_WHITELIST`, `TIMBRE_NS_BLACKLIST` env vars)
* Statically using: `timbre/set-config!` /`timbre/merge-config!`
* Dynamically using: `timbre/with-config`
2015-05-29 05:37:52 +00:00
2015-07-29 06:42:19 +00:00
There are also variants of the core logging macros that take an **explicit config arg** :
```clojure
(timbre/log* < config-map > < level > < & args>) ; or
(timbre/logf* < config-map > < level > < & args>)
```
2013-11-30 10:03:04 +00:00
2015-05-29 05:37:52 +00:00
Logging calls excluded by a compile-time option (e.g. during Cljs compilation) will be **entirely elided from your codebase** , e.g.:
```bash
#!/bin/bash
# edn values welcome:
export TIMBRE_LEVEL=':warn' # Elide all lower logging calls
export TIMBRE_NS_WHITELIST='["my-app.*"]' # Elide all other ns logging calls
export TIMBRE_NS_BLACKLIST='["my-app.foo" "my-app.bar.*"]'
lein cljsbuild once # Compile js with appropriate logging calls excluded
lein uberjar # Compile jar ''
```
2013-06-01 12:41:07 +00:00
### Built-in appenders
2012-07-13 10:07:23 +00:00
2016-01-14 05:17:22 +00:00
#### Redis ([Carmine]) appender (v3+)
2012-07-13 10:07:23 +00:00
```clojure
2016-01-14 05:17:22 +00:00
;; [com.taoensso/carmine < latest-version > ] ; Add to project.clj deps
2015-02-23 14:49:32 +00:00
;; (:require [taoensso.timbre.appenders (carmine :as car-appender)]) ; Add to ns
2013-12-01 09:49:31 +00:00
2015-08-11 12:53:03 +00:00
(timbre/merge-config! {:appenders {:carmine (car-appender/carmine-appender)}})
2012-07-13 10:07:23 +00:00
```
2013-12-01 09:49:31 +00:00
This gives us a high-performance Redis appender:
2016-01-14 05:17:22 +00:00
* **All raw logging args are preserved** in serialized form (**even errors!**)
* Only the most recent instance of each **unique entry** is kept (hash fn used to determine uniqueness is configurable)
* Configurable number of entries to keep per log level
* **Log is just a value**: a vector of Clojure maps: **query+manipulate with standard seq fns** : group-by hostname, sort/filter by ns & severity, explore exception stacktraces, filter by raw arguments, stick into or query with **Datomic** , etc.
2013-12-01 09:49:31 +00:00
A simple query utility is provided: `car-appender/query-entries` .
2016-01-14 05:17:22 +00:00
#### Email ([Postal]) appender
2012-05-28 10:49:54 +00:00
```clojure
2016-01-14 05:17:22 +00:00
;; [com.draines/postal < latest-version > ] ; 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
2015-05-26 05:43:22 +00:00
(timbre/merge-config!
2015-08-11 12:53:03 +00:00
{:appenders
{:postal
(postal-appender/postal-appender
^{: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-12-01 09:49:31 +00:00
#### File appender
2013-04-19 13:28:02 +00:00
2013-11-30 13:19:56 +00:00
```clojure
2015-06-10 22:05:29 +00:00
;; (:require [taoensso.timbre.appenders.core :as appenders]) ; Add to ns
2015-05-26 05:43:22 +00:00
(timbre/merge-config!
2015-06-10 22:05:29 +00:00
{:appenders {:spit (appenders/spit-appender {:fname "/path/my-file.log"})}})
2013-11-30 13:19:56 +00:00
```
2013-06-05 12:52:23 +00:00
2013-11-30 13:19:56 +00:00
#### Other included appenders
2012-05-28 10:49:54 +00:00
2015-05-26 11:15:28 +00:00
A number of 3rd-party appenders are included out-the-box [here ](https://github.com/ptaoussanis/timbre/tree/master/src/taoensso/timbre/appenders/3rd_party ). **Please see the relevant docstring for details** . Thank you to the 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
2015-05-26 05:43:22 +00:00
## Profiling (currently Clj only)
2012-07-03 11:48:00 +00:00
2016-01-14 05:17:22 +00:00
The usual recommendation for Clojure profiling is: use a good **JVM profiler** like [YourKit], [JProfiler], or [VisualVM].
2012-07-03 11:48:00 +00:00
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.
2015-05-26 14:43:28 +00:00
Timbre profiling is fully **log level & ns filter aware** : if the level is insufficient or ns filtered, you **won't pay for profiling** .
2012-07-03 11:48:00 +00:00
2015-05-26 14:43:28 +00:00
And since `p` and `profile` **always return their body's result** , it becomes feasible to use profiling more often as part of your normal workflow: just *leave profiling code in production as you do logging code* .
2012-07-03 11:48:00 +00:00
2015-05-26 14:43:28 +00:00
A simple sampling profiler is also included.
2012-05-28 08:13:11 +00:00
2016-01-14 05:17:22 +00:00
## This project supports the ![ClojureWerkz-logo] goals
2013-06-01 12:41:07 +00:00
2016-01-14 05:17:22 +00:00
* [ClojureWerkz] is a growing collection of open-source, **batteries-included Clojure libraries** that emphasise modern targets, great documentation, and thorough testing.
2012-06-16 06:53:38 +00:00
2016-01-14 05:17:22 +00:00
## Contacting me / contributions
2012-11-05 17:48:42 +00:00
2016-01-14 05:17:22 +00:00
Please use the project's [GitHub issues page] for all questions, ideas, etc. **Pull requests welcome** . See the project's [GitHub contributors page] for a list of contributors.
2014-02-25 07:38:19 +00:00
2016-01-14 05:17:22 +00:00
Otherwise, you can reach me at [Taoensso.com]. Happy hacking!
2012-05-28 08:13:11 +00:00
2016-01-14 05:17:22 +00:00
\- [Peter Taoussanis]
2012-05-28 08:13:11 +00:00
## License
2016-01-14 05:17:22 +00:00
Distributed under the [EPL v1.0] \(same as Clojure).
Copyright © 2015-2016 [Peter Taoussanis].
2014-02-22 18:32:35 +00:00
2016-01-14 05:17:22 +00:00
<!-- - Standard links -->
[Taoensso.com]: https://www.taoensso.com
[Peter Taoussanis]: https://www.taoensso.com
[@ptaoussanis]: https://www.taoensso.com
[More by @ptaoussanis ]: https://www.taoensso.com
2015-03-20 08:20:23 +00:00
[Break Version]: https://github.com/ptaoussanis/encore/blob/master/BREAK-VERSIONING.md
2015-12-22 06:17:13 +00:00
2016-01-14 05:17:22 +00:00
<!-- - Standard links (repo specific) -->
[CHANGELOG]: https://github.com/ptaoussanis/timbre/releases
[API]: http://ptaoussanis.github.io/timbre/
[GitHub issues page]: https://github.com/ptaoussanis/timbre/issues
[GitHub contributors page]: https://github.com/ptaoussanis/timbre/graphs/contributors
[EPL v1.0]: https://raw.githubusercontent.com/ptaoussanis/timbre/master/LICENSE
[Hero]: https://raw.githubusercontent.com/ptaoussanis/timbre/master/hero.png "Title"
<!-- - Unique links -->
[Logs as Clojure values]: #redis -carmine-appender-v3
2015-12-22 06:17:13 +00:00
[@palletops/log-config]: https://github.com/palletops/log-config
[@fzakaria/slf4j-timbre]: https://github.com/fzakaria/slf4j-timbre
2016-01-14 05:17:22 +00:00
[tools.logging]: https://github.com/clojure/tools.logging
[io.aviso.exception]: https://github.com/AvisoNovate/pretty
[Carmine]: https://github.com/ptaoussanis/carmine
[Postal]: https://github.com/drewr/postal
[YourKit]: http://www.yourkit.com/)
[JProfiler]: http://www.ej-technologies.com/products/jprofiler/overview.html
[VisualVM]: http://docs.oracle.com/javase/6/docs/technotes/guides/visualvm/index.html
[ClojureWerkz-logo]: https://raw.github.com/clojurewerkz/clojurewerkz.org/master/assets/images/logos/clojurewerkz_long_h_50.png
[ClojureWerkz]: http://clojurewerkz.org/