Liking cljdoc? Tell your friends :D

shimmer ✨

CI

An extremely lightweight ClojureScript logging library with compile-time optimization and customisable console output.

note

This library was built for the precise needs of a specific project, it's highly opinionated (in its minuteness) and mainly it's just a way to facilitate a pattern rather than encapsulate functionality (the whole library is roughly 100 lines of clj/cljs). If you're looking for a more complete, flexible logging solution, there are other amazing projects like glogi, telemere, or timbre.

Installation

Add to your deps.edn:

{:deps {net.clojars.bru/shimmer {:mvn/version "0.1.0"}}}

Or with shadow-cljs in shadow-cljs.edn:

:dependencies [[net.clojars.bru/shimmer "0.1.0"]]

Quick Start

(ns my-app.core
  (:require [shimmer.core :refer [log]]))

;; Basic logging
(log :info "Application started")
(log :debug "Processing item" :item-id 123 :count 5)
(log :warn "Rate limit approaching" :current 95 :max 100)
(log :error "Connection failed" :host "api.example.com")

Log Levels

Shimmer supports six log levels, ordered from least to most verbose:

:off (0) < :error (1) < :warn (2) < :info (3) < :debug (4) < :all (5)

Configuration

Shimmer provides two-phase filtering for truly zero-overhead logging:

  1. Compile-time filtering via *log-filter-fn* - logs that don't pass are never compiled
  2. Runtime dead code elimination via Closure Compiler - remaining logs are eliminated in production builds by default (but can be forced, see below)

Custom Filter Function

Control which logs are emitted at compile time. Logs that don't pass the filter are never compiled into the code.

(ns my-app.logging
  (:require [shimmer.core :as shimmer]))

;; Define your filter
(defn my-filter-fn [namespace level]
  ;; Your logic here - return true to log, false to eliminate
  (case namespace
    "my-app.core" (shimmer/level-enabled? level :info)
    "my-app.debug" true
    false))

;; Install it (do this before any logging occurs)
(alter-var-root #'shimmer/*log-filter-fn* (constantly my-filter-fn))

Example: Hierarchical namespace filtering

(defn hierarchical-filter [namespace level]
  "Allow :info and above for my-app.*, :debug for my-app.dev.*"
  (cond
    (string/starts-with? namespace "my-app.dev.")
    (shimmer/level-enabled? level :debug)

    (string/starts-with? namespace "my-app.")
    (shimmer/level-enabled? level :info)

    :else false))

Custom Log Function

Replace the default console output with your own implementation:

; naive logger that just outputs the message and ignores any additional data
; please don't do this.
(defn custom-logger [_level _logger-namespace message _additional-data]
  (my-fancy-logger message))

(alter-var-root #'shimmer/*log-fn* (constantly custom-logger))

Performance Timing

Shimmer includes built-in support for relative timing:

(require '[shimmer.core :as log]
         '[shimmer.format :as fmt])

;; Set reference point
(fmt/set-epoch!)

;; All subsequent logs will show milliseconds since epoch
(log/log :info "Starting process")  ; :time 0
(do-work)
(log/log :info "Process complete")  ; :time X (X ms elapsed)

;; Reset to new reference point
(fmt/set-epoch!) ; time is reset to 0 

API Reference

Macros

log

(log level message & keyvals)

Logs a message at the specified level with optional key-value pairs.

Parameters:

  • level - Keyword: :trace, :debug, :info, :warn, :error, :off, or :all
  • message - String describing the event
  • keyvals - Optional alternating keys and values for additional context

Note: log with a level of :off will never be displayed

Example:

(log/log :info "User action" :user-id 123 :action "login" :ip "192.168.1.1")

(log/log :off "Disabled logging message" :user-id 321) ; this message will never be printed

Dynamic Vars

*log-filter-fn*

Clojure function (fn [level namespace] -> boolean) called at compile time to determine if a log statement should be included in the compiled output.

*log-fn*

Clojurescript function (fn [level namespace message-string kv-map] -> nil) called at runtime to perform the actual logging.

Closure Defines

shimmer.core/FORCE-LOGGING

Boolean Closure Define (default: false) that controls runtime dead code elimination. When false, logs are eliminated in production builds (when goog.DEBUG is also false). Set to true in your build config to force logs to appear even in production (note that the *log-filter-fn* still applies, so only logs for which *log-filter-fn returned true would appear).

Configure in your shadow-cljs.edn:

:closure-defines {shimmer.core/FORCE-LOGGING true}

Functions

compare-levels

(compare-levels level1 level2) -> integer

Compares two log levels. Returns a positive integer if level1 is more verbose than level2, zero if equal, negative if less verbose. This is a standard comparator function compatible with Clojure's sort and comparison functions.

Example:

(shimmer/compare-levels :debug :info)  ; => 1  (debug is more verbose)
(shimmer/compare-levels :error :warn)  ; => -1 (error is less verbose)
(shimmer/compare-levels :info :info)   ; => 0  (equal)

level-enabled?

(level-enabled? level threshold) -> boolean

Returns true if a log level should be enabled given a threshold. A level is enabled if it is less than or equal to the threshold in verbosity. This is the recommended function for level comparison in config resolvers.

Example:

(shimmer/level-enabled? :info :debug)  ; => true  (info should be logged)
(shimmer/level-enabled? :debug :info)  ; => false (debug filtered out)
(shimmer/level-enabled? :warn :warn)   ; => true  (equal levels enabled)

set-epoch!

(shimmer.format/set-epoch!)           ; Use current time
(shimmer.format/set-epoch! timestamp) ; Use specific timestamp

Sets a reference timestamp for relative timing in logs. Available in the shimmer.format namespace.

simple-log

(shimmer.format/simple-log name format kv-map)

Default logging implementation that outputs to browser console with colored namespace prefixes. Available in the shimmer.format namespace.

License

Eclipse Public License 1.0

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close