Claude
Skills
Sign in
Back

timbre

Included with Lifetime
$97 forever

Pure Clojure/Script logging library with flexible configuration and powerful features

General

What this skill does


# Timbre

Pure Clojure/Script logging library with zero dependencies, simple configuration, and powerful features like async logging, rate limiting, and flexible appenders.

## Overview

Timbre is a logging library designed for Clojure and ClojureScript applications. Unlike Java logging frameworks requiring XML or properties files, Timbre uses pure Clojure data structures for all configuration.

**Key characteristics:**
- Full Clojure and ClojureScript support
- Single config map - no XML/properties files
- Zero overhead compile-time level/namespace elision
- Built-in async logging and rate limiting
- Function-based appenders and middleware
- Optional tools.logging and SLF4J interop

**Library:** `com.taoensso/timbre`
**Latest Version:** 6.8.0
**License:** EPL-1.0

**Note:** For new projects, consider [Telemere](https://github.com/taoensso/telemere) - a modern rewrite of Timbre. Existing Timbre users have no pressure to migrate.

## Installation

```clojure
;; deps.edn
{:deps {com.taoensso/timbre {:mvn/version "6.8.0"}}}

;; Leiningen
[com.taoensso/timbre "6.8.0"]
```

## Core Concepts

### Log Levels

Seven standard levels in ascending severity:

- `:trace` - Detailed diagnostic information
- `:debug` - Debugging information
- `:info` - Informational messages
- `:warn` - Warning messages
- `:error` - Error messages
- `:fatal` - Critical failures
- `:report` - Special reporting level

### Appenders

Functions that handle log output: `(fn [data]) -> ?effects`

Each appender receives a data map containing:
- `:level` - Log level keyword
- `:?msg` - Log message
- `:timestamp` - When log occurred
- `:hostname` - System hostname
- `:?ns-str` - Namespace string
- `:?file`, `:?line` - Source location
- `:?err` - Exception (if present)
- Additional context data

### Middleware

Functions that transform log data: `(fn [data]) -> ?data`

Applied before appenders receive data, enabling:
- Data enrichment
- Filtering
- Transformation
- Context injection

### Configuration

Timbre uses a single atom `*config*` containing:
- `:min-level` - Minimum log level
- `:ns-filter` - Namespace filtering
- `:middleware` - Middleware functions
- `:timestamp-opts` - Timestamp formatting
- `:output-fn` - Output formatter
- `:appenders` - Appender map

## Basic Usage

### Simple Logging

```clojure
(require '[taoensso.timbre :as timbre])

;; Basic logging at different levels
(timbre/trace "Entering function")
(timbre/debug "Variable value:" x)
(timbre/info "Server started on port" port)
(timbre/warn "Deprecated function used")
(timbre/error "Failed to connect to database")
(timbre/fatal "Critical system failure")
(timbre/report "Daily metrics" {:users 1000 :requests 5000})
```

### Formatted Logging

```clojure
;; Printf-style formatting
(timbre/infof "User %s logged in from %s" username ip)
(timbre/warnf "Cache miss rate: %.2f%%" miss-rate)
(timbre/errorf "Request failed: %d %s" status-code message)
```

### Logging with Exceptions

```clojure
(try
  (risky-operation)
  (catch Exception e
    (timbre/error e "Operation failed")))

;; Multiple values
(timbre/error e "Failed processing" {:user-id 123 :item-id 456})
```

### Spy - Log and Return

```clojure
;; Log value and return it
(let [result (timbre/spy :info (expensive-calculation x y))]
  (process result))

;; With custom message
(timbre/spy :debug
  {:msg "Calculation result"}
  (expensive-calculation x y))
```

## Configuration

### Setting Minimum Level

```clojure
;; Global minimum level
(timbre/set-min-level! :info)

;; Per-namespace level
(timbre/set-ns-min-level! {:deny #{"noisy.namespace.*"}
                            :allow #{"important.namespace.*"}})
```

### Modifying Config

```clojure
;; Replace entire config
(timbre/set-config! new-config)

;; Merge into existing config
(timbre/merge-config! {:min-level :debug
                        :appenders {:println (println-appender)}})

;; Swap with function
(timbre/swap-config! update :min-level (constantly :warn))
```

### Scoped Configuration

```clojure
;; Temporarily change config
(timbre/with-config custom-config
  (timbre/info "Logged with custom config"))

;; Merge temporary changes
(timbre/with-merged-config {:min-level :trace}
  (timbre/trace "Temporarily enabled trace logging"))

;; Temporary level change
(timbre/with-min-level :debug
  (timbre/debug "Debug enabled for this scope"))
```

## Appenders

### Built-in Appenders

#### Console Output

```clojure
(require '[taoensso.timbre.appenders.core :as appenders])

;; println appender (default)
{:appenders {:println (appenders/println-appender)}}
```

#### File Output

```clojure
;; Simple file appender
{:appenders {:spit (appenders/spit-appender
                     {:fname "/var/log/app.log"})}}
```

### Custom Appender

```clojure
(defn my-appender
  "Appender that writes to a custom destination"
  [opts]
  {:enabled? true
   :async? false
   :min-level nil  ; Inherit from config
   :rate-limit nil
   :output-fn :inherit
   :fn (fn [data]
         (let [{:keys [output-fn]} data
               formatted (output-fn data)]
           ;; Custom logic here
           (send-to-custom-system formatted)))})

;; Use custom appender
(timbre/merge-config!
  {:appenders {:custom (my-appender {})}})
```

### Appender Configuration Options

```clojure
{:enabled? true        ; Enable/disable
 :async? false         ; Async logging?
 :min-level :info      ; Appender-specific min level
 :rate-limit [[5 1000]] ; Max 5 logs per 1000ms
 :output-fn :inherit   ; Use config's output-fn
 :fn (fn [data] ...)}  ; Handler function
```

## Advanced Features

### Context (MDC)

```clojure
;; Set context for current thread
(timbre/with-context {:user-id 123 :request-id "abc"}
  (timbre/info "Processing request")
  (do-work))

;; Add to existing context
(timbre/with-context+ {:session-id "xyz"}
  (timbre/info "Additional context"))
```

### Middleware

```clojure
;; Add hostname to all logs
(defn add-hostname-middleware
  [data]
  (assoc data :hostname (get-hostname)))

;; Add timestamp middleware
(defn add-custom-timestamp
  [data]
  (assoc data :custom-ts (System/currentTimeMillis)))

;; Apply middleware
(timbre/merge-config!
  {:middleware [add-hostname-middleware
                add-custom-timestamp]})
```

### Rate Limiting

```clojure
;; Per-appender rate limit
{:appenders
 {:println
  {:enabled? true
   :rate-limit [[10 1000]  ; Max 10 per second
                [100 60000]] ; Max 100 per minute
   :fn (fn [data] (println (:output-fn data)))}}}

;; At log call site
(timbre/log {:rate-limit [[1 5000]]}  ; Max once per 5 seconds
            :info "Rate limited message")
```

### Async Logging

```clojure
;; Enable async for appender
{:appenders
 {:async-file
  {:enabled? true
   :async? true  ; Process logs asynchronously
   :fn (fn [data]
         (spit "/var/log/app.log"
               (str (:output-fn data) "\n")
               :append true))}}}
```

### Conditional Logging

```clojure
;; Log only sometimes (probabilistic)
(timbre/sometimes 0.1  ; 10% probability
  (timbre/info "Sampled log message"))

;; Conditional error logging
(timbre/log-errors
  (risky-operation))  ; Logs if exception thrown

;; Log and rethrow
(timbre/log-and-rethrow-errors
  (risky-operation))  ; Logs then rethrows exception
```

### Exception Handling

```clojure
;; Capture uncaught JVM exceptions (Clojure only)
(timbre/handle-uncaught-jvm-exceptions!)

;; Log errors in futures
(timbre/logged-future
  (risky-async-operation))
```

## Output Formatting

### Custom Output Function

```clojure
(defn my-output-fn
  [{:keys [level ?ns-str ?msg-fmt vargs timestamp_ ?err]}]
  (str
    (force timestamp_) " "
    (str/upper-case (name level)) " "
    "[" ?ns-str "] - "
    (apply format ?msg-fmt vargs)
    (when ?err (str "\n" (timbre/stacktrace ?err)))))

;; Use custom output
(timbre/merge-config! {:output-fn my-output-fn})
```

### Timestamp Configuration

```clojure
{:timestamp-opts
 {:pattern "yyyy-MM-dd HH:mm:ss.SSS"
  :locale (java.util.Locale/getDefaul

Related in General