Clojure wrapper for SLF4j with Mapped Diagnostic Context (MDC) and clojure/tools.logging.
Leiningen coordinates: [cambium "0.8.1"]
Note: Cambium only wraps over SLF4j. You also need a suitable SLF4j implementation, such as logback-bundle as your project dependency.
Require the namespace:
(require '[cambium.core :as c])
(require '[cambium.mdc :as m])
Like clojure.tools.logging/<log-level>
, Cambium defines namespace loggers for various levels:
(c/info "this is a log message") ; simple message logging
(c/info {:latency-ms 331 :module "registration"} "App registered") ; context and message
(c/debug {:module "order-processing"} "sequence-id verified")
(c/error {:module "user-feedback"} exception "Email notification failed") ; context, exception and message
Available log levels: trace
, debug
, info
, warn
, error
, fatal
You can define custom loggers that you can use from any namespace as follows:
(c/deflogger metrics "METRICS")
(c/deflogger txn-log "TXN-LOG" :info :fatal)
(metrics {:latency-ms 331 :module "registration"} "app.registration.success") ; context and message
(txn-log {:module "order-processing"} exception "Stock unavailable") ; context, exception and message
(txn-log "Order processed") ; simple message logging
Value based context can be propagated as follows:
;; Propagate specified context in current thread
(c/with-logging-context {:user-id "X1234"}
...
(c/info {:job-id 89} "User was assigned a new job")
...)
;; wrap an existing fn with specified context
(c/wrap-logging-context {:user-id "X1234"} user-assign-job) ; creates a wrapped fn that inherits specified context
Unlike value based propagation MDC propagation happens wholesale, i.e. the entire current MDC map is replaced with a new map. Also, no conversion is applied to MDC; they are required to have string keys and values. See example below:
;; Propagate specified context in current thread
(m/with-raw-mdc {"userid" "X1234"}
...
(c/info {:job-id 89} "User was assigned a new job")
...)
;; wrap an existing fn with specified context
(m/wrap-raw-mdc user-assign-job) ; creates a wrapped fn that inherits current context
(m/wrap-raw-mdc {"userid" "X1234"} user-assign-job) ; creates a wrapped fn that inherits specified context
Note: This requires special logging layout implementation that can decode the encoded context.
Context values sometimes may be nested and need manipulation. Cambium requires format-preserving codec to be configured ahead of using nested context:
(alter-var-root #'cambium.core/stringify-val (constantly cambium.nested/encode-val)
(alter-var-root #'cambium.core/destringify-val (constantly cambium.nested/decode-val)
If you are logging via JSON, you may want to override Cambium fns with the JSON converter functions.
For first-class handling of nested context, Cambium can convert all tokens in a key path as string tokens. This requires special configuration as follows:
(alter-var-root #'cambium.core/stringify-val (constantly cambium.nested/encode-val)
(alter-var-root #'cambium.core/destringify-val (constantly cambium.nested/decode-val)
(alter-var-root #'cambium.core/context-val (constantly cambium.nested/nested-context-val)
(alter-var-root #'cambium.core/merge-logging-context! (constantly cambium.nested/merge-nested-context!)
Now see nesting-navigation example below:
(c/with-logging-context {:order {:client "XYZ Corp"
:item-count 10}}
;; ..other processing..
(c/with-logging-context {[:order :id] "F-123456"}
;; here the context will be {"order" {"client" "XYZ Corp" "item-count" 10 "id" "F-123456"}}
(c/info "Order processed successfully")))
;; Logging API in the 'nested' namespace accepts nested MDC
(c/info {:order {:event-id "foo"}} "Foo happened")
Copyright © 2015-2017 Shantanu Kumar (kumar.shantanu@gmail.com, shantanu.kumar@concur.com)
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close