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.
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"]]
(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")
Shimmer supports six log levels, ordered from least to most verbose:
:off (0) < :error (1) < :warn (2) < :info (3) < :debug (4) < :all (5)
Shimmer provides two-phase filtering for truly zero-overhead logging:
*log-filter-fn* - logs that don't pass are never compiledControl 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))
(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))
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))
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
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 :allmessage - String describing the eventkeyvals - Optional alternating keys and values for additional contextNote: 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
*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.
shimmer.core/FORCE-LOGGINGBoolean 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}
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.
Eclipse Public License 1.0
Can you improve this documentation?Edit 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 |