Liking cljdoc? Tell your friends :D

clojure.log4j2

When clojure.tools.logging is not enough

Clojars Project

Features

  • legacy Java logging libs work, so JUL,JCL,log4j etc etc all get logged with the same config
  • log data (clojure maps & more). and optionally strings
  • use builder api avoid varargs/positional-args confusion in API with Throwable etc
  • bit of help with programmatic config, as alternative to xml files
  • clojure.tools.logging still works.. so you don't have to move off it in one go

Rationale

'Log data, not strings' is a thing, but of all the Java Logging frameworks, log4j2 is the only one that actually does that. In all logging frameworks you can format messages as json or something and get {level: "INFO", message "bar"}, but what is different with log4j2 is that you can log arbitrary data and have that data passed as-is to appenders, such as nosql appenders. So the console appender might format that data as JSON, but the mongo appender will persist a mongo object created directly from the data, not a string representation of it.

clojure.tools.logging can route log statements through many java logging systems including log4j2. However, the args to log functions are stringified before calling the underlying impl so it is not suitable for logging data.

The same limitation also exists in pedestal.log

But what about...

Timbre

I haven't used that personally. It might be great, others seem less keen - see Clojureverse thread below for example.

Mulog

Feature-wise that seems to be the same as log4j2, so to consider that I'd be looking for some compelling reasons to move away from what I consider the safe choice of log4j2.

Usage

Logging

(ns my.ns
  (:require [com.widdindustries.log4j2.log-api :as log])
  (:import [org.apache.logging.log4j.message MapMessage]))

;log string
(log/info "hello")

;log a Message - this is how you 'log data'
(log/info (MapMessage. {"foo" "bar"}))

;clojure maps wrapped in MapMessage object - top level keys must be Named (string, keyword, symbol etc)
(log/info {"foo" "bar"})

; varargs arity is for formatted string only
(log/info "hello {} there" :foo)

; builder - include throwable|marker|location
(-> (log/info-builder)
    (log/with-location)
    (log/with-throwable *e)
    ; finally log string or Message etc
    (log/log {"foo" "bar"}))

; change log level to trace
(log/set-level 'my.ns :trace)

Config

Configure log4j2 with xml etc as you like, but if you prefer to do it programmatically, there is a little sugar in this lib that might help a bit.

Configure logger before logging

(ns my.ns
  (:require [com.widdindustries.log4j2.config :as config]))

; the equivalent of having magic xml file on classpath
(defn setup-logging []
  (let [builder (config/builder)
        std-out-appender-name "Stdout"]
    (-> builder
        (.add (config/std-out-appender builder std-out-appender-name
                "%date %level %logger %message%n%throwable"))
        (.add (config/root-logger builder org.apache.logging.log4j.Level/INFO std-out-appender-name))
        (.add (config/logger builder Level/DEBUG std-out-appender-name "my.ns"))
        ;(.writeXmlConfiguration  System/out)
        (config/start))))

Release

create a git tag.

make install VERSION=your-tag (this installs in ~/.m2 - check that things look ok)

make deploy VERSION=your-tag - you need to have set up clojars credentials as per https://github.com/applied-science/deps-library

git push origin new-tag-name

References

Copyright © 2021 Widd Industries

Can you improve this documentation?Edit on GitHub

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

× close