
API | Slack channel | Latest release: v1.2.0 (2026-07-26)
Trove is a minimal, modern alternative to tools.logging that supports:
It's intended mostly for library authors that want to emit rich logging without forcing their users to adopt any particular backend (Telemere, Timbre, μ/log, tools.logging, SLF4J, etc.).
Trove is tiny (0 deps, ~250 loc), fast, and highly flexible.
A data-oriented pipeline allows easier filtering, transformation, and analysis. It's also often faster since it helps avoid unnecessary serialization, and is well suited to the tools and idioms offered by Clojure and ClojureScript.
Trove just works out the box: if a library you use depends on Trove, you'll automatically get sensible library logging to *out* or the JS console. No need for any config, or even a logging library.
But you may prefer to configure Trove to instead direct logs to your preferred backend by calling trove/set-log-fn!:
(ns my-ns
(:require
[taoensso.trove.x] ; x ∈ #{console telemere timbre mulog tools-logging slf4j} (default console)
[taoensso.trove :as trove]))
(trove/set-log-fn! (taoensso.trove.x/get-log-fn {:bridge-ctx? true/false}))
(trove/set-log-fn! nil) ; To noop all `trove/log!` calls
Use
{:bridge-ctx? true}if you'd like to allow library authors to update your backend's native context when relevant. Requires Telemere, Timbre (except on Babashka), μ/log, or SLF4J with an MDC-capable provider.
You can also easily write your own log-fn - see trove/*log-fn* for details.
Include Trove in your library deps, then use trove/log! to make your logging calls:
(ns my-ns (:require [taoensso.trove :as trove]))
(trove/log! {:level :info, :id :auth/login, :data {:user-id 1234}, :msg "User logged in!"})
Trove uses the same map-based logging API as Telemere. The above expands to roughly:
(when-let [log-fn trove/*log-fn*] ; Chosen backend fn
(log-fn "my-ns" [line column] :info :auth/login ; Callsite info
{:msg "User logged in!", :data {:user-id 1234}})) ; Payload
The end user's chosen backend takes care of filtering and output.
Structured logging sometimes involves expensive data collection or transformation, e.g.:
(trove/log! {:id ::my-event, :data (expensive) ...})
That's why Trove automatically delays payload values that need runtime evaluation, allowing the backend to apply filtering before paying realization costs. See *log-fn* for payload details.
The trove/*ctx* context map may be attached to trove/log! output:
(trove/with-ctx+ {:workflow/step "fetch-page"}
(trove/log! {:id :ingest/fetched, :data {:n 42}}))
Utils:
set-root-ctx! to modify root (default) contextwith-ctx to replace the current contextwith-ctx+ to update the current context
log!also takes:ctxand:ctx+options to replace or update the context for a single call.
The above context belongs to Trove, so affects only trove/log! calls.
Some backends have their own equivalent context, which you can also update for end-users that choose to opt-in:
(trove/with-ctx+ {:workflow/step "fetch-page"}
(trove/with-ctx-bridge ; Merge Trove context into native backend context
(fetch-page)))
Custom backends can add bridge support with trove/add-ctx-bridge.
You can help support continued work on this project and others, thank you!! 🙏
Copyright © 2026 Peter Taoussanis.
Licensed under EPL 1.0 (same as Clojure).
Can you improve this documentation? These fine people already did:
Peter Taoussanis & Michiel BorkentEdit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |