Liking cljdoc? Tell your friends :D

Taoensso open source
API | Slack channel | Latest release: v1.0.0-beta1 🚧 (2025-06-19)

Clj tests Cljs tests Graal tests

Trove

Modern logging facade for Clojure/Script

Trove is a minimal, modern alternative to tools.logging that supports:

  • Both traditional and structured logging
  • Both Clojure and ClojureScript
  • Richer filtering capabilities (by namespace, id, level, data, etc.)

It's TINY (1 macro, 0 deps, ~100 loc), fast, and highly flexible.

It supports any backend including: Telemere, Timbre, μ/log, tools.logging, SLF4J, a custom fn, etc.

It works great for library authors that want to emit rich logging without forcing their users to adopt any particular backend.

To log

  1. Include the (tiny) dependency in your project or library.
  2. Use trove/log! to make your logging calls (see docstring for API):
(ns my-ns (:require [taoensso.trove :as trove]))

(trove/log! {:level :info, :id :auth/login, :data {:user-id 1234}, :msg "User logged in!"})

The above logging call expands to:

(when-let [log-fn trove/*log-fn*] ; Chosen backend fn
  (log-fn ... "my-ns" :info :auth/login [line-num column-num]
    {:msg "User logged in!", :data {:user-id 1234}} ...))

And the chosen backend then takes care of filtering and output.

To choose a backend

Just modify trove/*log-fn* (see docstring for API).

The default log-fn prints logs to *out* or the JS console. Alternatives are provided for some common backends:

(ns my-ns
  (:require
   [taoensso.trove :as trove]

   ;; Choose (uncomment) one:
   ;; [taoensso.trove.console       :as trove-backend] ; default
   ;; [taoensso.trove.telemere      :as trove-backend]
   ;; [taoensso.trove.timbre        :as trove-backend]
   ;; [taoensso.trove.mulog         :as trove-backend]
   ;; [taoensso.trove.tools-logging :as trove-backend]
   ;; [taoensso.trove.slf4j         :as trove-backend]
   ))

(trove/set-log-fn! (trove-backend/get-log-fn {}))

It's easy to write your own log-fn if you want to use a different backend or customise anything.

What about expensive data?

Structured logging sometimes involves expensive data collection or transformation, e.g.:

(trove/log! {:id ::my-event, :data (expensive) ...})

That's why Trove automatically delays any values that need runtime evaluation, allowing the backend to apply filtering before paying realization costs.

This explains the :lazy_ {:keys [msg data error kvs]} arg given to truss/*log-fn*.

Funding

You can help support continued work on this project and others, thank you!! 🙏

License

Copyright © 2025 Peter Taoussanis.
Licensed under EPL 1.0 (same as Clojure).

Can you improve this documentation?Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close