CHANGELOG | API | current Break Version:
[com.taoensso/timbre "6.2.1"] ; See CHANGELOG for details
See here if to help support my open-source work, thanks! - Peter Taoussanis
Timbre: a pure Clojure/Script logging library
Getting even the simplest Java logging working can be maddeningly complex, and it often gets worse at scale as your needs become more sophisticated.
Timbre offers an all Clojure/Script alternative that's fast, deeply flexible, easy to configure with pure Clojure data, and that just works out the box.
Supports optional interop with tools.logging and log4j/logback/slf4j.
Happy hacking!
Features
- Full Clojure & ClojureScript support, with built-in appenders for both.
- A single, simple config map, and you're set. No need for XML or properties files.
- Simple
(fn [data]) -> ?effects
appenders, and(fn [data]) -> ?data
middleware. - Easily save raw logging arguments to the DB of your choice.
- Easily filter logging calls by any combination of: level, namespace, appender.
- Zero overhead compile-time level/ns elision.
- Powerful, easy-to-configure rate limits and async logging.
- Great performance and flexibility at any scale.
- Small, simple, cross-platform pure-Clojure codebase.
Quickstart
Add the necessary dependency to your project:
Leiningen: [com.taoensso/timbre "6.2.1"] ; or
deps.edn: com.taoensso/timbre {:mvn/version "6.2.1"}
And setup your namespace imports:
(ns my-ns
(:require
[taoensso.timbre :as timbre
;; Optional, just refer what you like:
:refer [log trace debug info warn error fatal report
logf tracef debugf infof warnf errorf fatalf reportf
spy]]))
You can also call
(timbre/refer-timbre)
(Clj only) to refer everything above automatically.
Basic logging
By default, Timbre gives you basic println
and js/console
output for all logging calls of at least :debug
log level:
(info "This will print") => nil
%> 15-Jun-13 19:18:33 localhost INFO [my-app.core] - This will print
(spy :info (* 5 4 3 2 1)) => 120
%> 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}
(trace "This won't print due to insufficient log level") => nil
First-argument exceptions will also generate a stack trace:
(info (Exception. "Oh no") "arg1" "arg2")
%> 15-Jun-13 19:22:55 localhost INFO [my-app.core] - arg1 arg2
java.lang.Exception: Oh no
<Stacktrace>
Set the minimum logging level
A Timbre logging call will be disabled (noop) when the call's level (e.g. (info ...)
is less than the active minimum level (e.g. :warn
).
Levels:
:trace
<:debug
<:info
<:warn
<:error
<:fatal
<:report
- Call
(set-min-level! <min-level>)
to set the minimum level for all namespaces. - Call
(set-ns-min-level! <min-level>)
to set the minimum level for the current namespace only.
See the config API for more.
Architecture
Timbre's inherently a simple design, no magic. It's just Clojure data and functions.
Here's the flow for an (info ...)
logging call:
- Dynamic
*config*
is used as the current active config. - Is
:info
< the active minimum level? If so, end here and noop. - Is the current namespace filtered? If so, end here and noop.
- Prepare a log data map of interesting info incl. all logging arguments.
- Pass the data map through any middleware fns:
(fn [data]) -> ?data
. These may transform the data. If returned data is nil, end here and noop. - Pass the data map to all appender fns:
(fn [data]) -> ?effects
. These may print output, save the data to a DB, trigger admin alerts, etc.
Configuration
Timbre's behaviour is controlled by the single dynamic *config*
map, fully documented here.
Its default value can be easily overridden by:
- An edn file on your resource path.
- A symbol defined by an an environment variable or JVM property.
- A variety of provided utils.
- Standard Clojure utils (
binding
,alter-var-root!
/set!
).
Sophisticated behaviour is achieved through normal fn composition, and the power of arbitrary Clojure fns: e.g. write to your database, send a message over the network, check some other state (e.g. environment config) before making a choice, etc.
Advanced minimum levels and namespace filtering
The *config*
:min-level
and :ns-filter
values both support sophisticated pattern matching, e.g.:
:min-level
:[[#{\"taoensso.*\"} :error] ... [#{\"*\"} :debug]]
.:ns-filter
:{:allow #{"*"} :deny #{"taoensso.*"}}
.
As usual, the full functionality is described by the config API.
Note that both :min-level
and :ns-filter
may also be easily overridden on a per-appender basis.
Compile-time elision
By setting the relevant JVM properties or environment variables, Timbre can actually entirely exclude the code for disabled logging calls at compile-time, e.g.:
#!/bin/bash
# Elide all lower-level logging calls:
export TAOENSSO_TIMBRE_MIN_LEVEL_EDN=':warn'
# Elide all other ns logging calls:
export TAOENSSO_TIMBRE_NS_PATTERN_EDN='{:allow #{"my-app.*"} :deny #{"my-app.foo" "my-app.bar.*"}}'
lein cljsbuild once # Compile js with appropriate logging calls excluded
lein uberjar # Compile jar ''
Disable stacktrace colors
ANSI colors are enabled by default for Clojure 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:
:output-opts {:stacktrace-fonts {}}
And/or you can set the:
taoensso.timbre.default-stacktrace-fonts.edn
JVM property, orTAOENSSO_TIMBRE_DEFAULT_STACKTRACE_FONTS_EDN
environment variable.
Included appenders
Basic file appender
;; (:require [taoensso.timbre.appenders.core :as appenders]) ; Add to ns
(timbre/merge-config!
{:appenders {:spit (appenders/spit-appender {:fname "/path/my-file.log"})}})
;; (timbre/merge-config! {:appenders {:spit {:enabled? false}}} ; To disable
;; (timbre/merge-config! {:appenders {:spit nil}} ; To remove entirely
Carmine (Redis) appender
;; [com.taoensso/carmine <latest-version>] ; Add to project.clj deps
;; (:require [taoensso.timbre.appenders (carmine :as car-appender)]) ; Add to ns
(timbre/merge-config! {:appenders {:carmine (car-appender/carmine-appender)}})
This gives us a high-performance Redis appender:
- All raw logging args are preserved in serialized form (even errors).
- Configurable number of entries to keep per log level.
- Only the most recent instance of each unique entry is kept.
- Resulting log is just a Clojure value: a vector of log entries (maps).
Clojure has a rich selection of built-in and community tools for querying values like this.
See also car-appender/query-entries
.
Postal (email) appender
;; [com.draines/postal <latest-version>] ; Add to project.clj deps
;; (:require [taoensso.timbre.appenders (postal :as postal-appender)]) ; Add to ns
(timbre/merge-config!
{:appenders
{:postal
(postal-appender/postal-appender
^{:host "mail.isp.net" :user "jsmith" :pass "sekrat!!1"}
{:from "[email protected]" :to "[email protected]"})}})
Community appenders
A number of community appenders are included with Timbre.
Thanks to the relevant authors! Please see appender namespace docstrings for details.
GitHub PRs very welcome for:
- Maintenance of any existing community appenders (thank you!!).
- Additional dependency-free appenders. (See example template).
- Additional links to externally-hosted appenders in the table below.
More community tools, appenders, etc.
Some externally-hosted items are listed here:
Link | Description |
---|---|
@fzakaria/slf4j-timbre | Route log4j/logback/sfl4j log output to Timbre |
@palletops/log-config | Library to help manage Timbre logging config |
Your link here? | PR's welcome! |
goals
This project supports theClojureWerkz is a growing collection of open-source, batteries-included Clojure libraries that emphasise modern targets, great documentation, and thorough testing.
Contacting me / contributions
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.
Otherwise, you can reach me at Taoensso.com. Happy hacking!
License
Distributed under the EPL v1.0 (same as Clojure).
Copyright © 2015-2023 Peter Taoussanis.