Timbre Logging Library

repository·master·Indexed 23 days ago

https://github.com/taoensso/timbre

A pure Clojure and ClojureScript logging library providing a data-driven alternative to traditional Java logging. It features configuration via Clojure maps, extensible appenders and middleware, compile-time elision for performance, and support for both JVM and JS environments. Timbre includes interop with tools.logging and SLF4Jv2, and provides specialized appenders for files, Redis (via Carmine), and email (via Postal).

Tokens
2.8K
Snippets
7
Records
19
Agent score
80%

What's inside Timbre

  1. Overview of Timbre logging library

    master

    Timbre is a pure Clojure and ClojureScript logging library designed to be fast, flexible, and easy to configure using pure Clojure data. It avoids the complexity of XML or properties-based Java logging configurations and works out of the box.

    Key features include:

    • Full support for both Clojure and ClojureScript with built-in appenders for both.
    • Configuration via a single, simple Clojure map.
    • Extensible via appenders (fn [data]) -> ?effects and middleware (fn [data]) -> ?data.
    • Ability to save raw logging arguments to databases.
    • Granular filtering by level, namespace, or appender.
    • Zero overhead compile-time level/namespace elision.
    • Support for rate limits and asynchronous logging.
    • Optional interop with tools.logging and Java logging via SLF4Jv2.
  2. Consider Telemere for new projects

    master
    While Timbre is still supported, it is recommended that new users use Telemere instead. Telemere is a modern rewrite of Timbre. Existing Timbre users can migrate to Telemere, and the process is described as quick and easy in the Telemere documentation.
  3. How Timbre's architecture works

    master

    Timbre follows a simple data-driven flow for every logging call (e.g., (info ...)):

    1. Config Check: Uses the dynamic *config* to determine the active configuration.
    2. Level Check: If the call's level is lower than the active minimum level, the call is a no-op.
    3. Namespace Filter: If the current namespace is filtered out, the call is a no-op.
    4. Data Preparation: A log data map is created containing the log level, namespace, and all arguments.
    5. Middleware: The data map is passed through middleware functions (fn [data]) -> ?data. If a middleware returns nil, the process stops.
    6. Appenders: The (potentially transformed) data map is passed to all appender functions (fn [data]) -> ?effects, which handle the actual output (printing, DB writes, etc.).
  4. Route legacy Java logging (Log4j, JUL, JCL) to Timbre via SLF4J

    master

    To route logging from legacy frameworks like Log4j, java.util.logging (JUL), or Apache Commons Logging (JCL) into Timbre, you must use an SLF4J bridge. The data flow follows this path:

    1. Legacy Framework (Log4j/JUL/JCL) $\rightarrow$ SLF4J Bridge $\rightarrow$ SLF4J API
    2. SLF4J API $\rightarrow$ Timbre (via timbre-slf4j)

    Ensure you have configured the SLF4J backend to use Timbre as described in the SLF4J setup guide.

  5. Use the Postal email appender

    master

    The Postal appender sends log entries via email using the postal library.

    Setup:

    1. Add [com.draines/postal <latest-version>] to your project dependencies.
    2. Require taoensso.timbre.appenders (postal :as postal-appender) in your namespace.
    3. Configure the appender with server credentials and email metadata (from/to) using postal-appender/postal-appender.
    ;; (:require [taoensso.timbre.appenders (postal :as postal-appender)])
    
    (timbre/merge-config!
      {:appenders
       {:postal
        (postal-appender/postal-appender
          ^{:host "mail.isp.net" :user "jsmith" :pass "sekrat!!1"}
          {:from "me@draines.com" :to "foo@example.com"})}})
  6. Use the Carmine Redis appender

    master

    The Carmine appender provides high-performance logging to Redis. It preserves all raw logging arguments in serialized form (including errors) and allows you to configure the number of entries kept per log level. It only keeps the most recent instance of each unique entry. The resulting log is a Clojure vector of log entry maps, which can be queried using standard Clojure tools or the car-appender/query-entries function.

    Setup:

    1. Add [com.taoensso/carmine <latest-version>] to your project dependencies.
    2. Require taoensso.timbre.appenders [carmine :as car-appender] in your namespace.
    3. Register it via timbre/merge-config!.
    ;; (:require [taoensso.timbre.appenders [carmine :as car-appender]])
    
    (timbre/merge-config! {:appenders {:carmine (car-appender/carmine-appender)}})
  7. Perform compile-time elision of logging calls

    master

    To reduce bundle size or improve performance, Timbre can exclude disabled logging calls at compile-time using JVM properties or environment variables. This is useful for production builds where you want to strip out :debug or :trace calls entirely.

    Environment Variables:

    • TAOENSSO_TIMBRE_MIN_LEVEL_EDN: Set the minimum level (e.g., ':warn').
    • TAOENSSO_TIMBRE_NS_PATTERN_EDN: Set the namespace filter pattern.
    # 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
  8. Use pre-bundled community appenders

    master
    Timbre comes with several community-contributed appenders pre-bundled in the taoensso.timbre.appenders.community namespace. For specific details on how to configure and use each one, refer to the docstrings within the respective appender namespace.
  9. Import Timbre into your namespace

    master

    To use Timbre, require it in your namespace. You can manually list the functions you want to refer, or use (timbre/refer-timbre) to automatically refer all standard logging functions.

    (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]]))
  10. Use the basic file appender

    master

    The spit appender writes log entries to a specified file. You can configure it using timbre/merge-config! by passing a map with the :appenders key and a :spit map containing the :fname option.

    To disable the appender, set :enabled? to false. To remove it entirely from the configuration, set it to nil.