Slog is a structured logging library, modeled after Go's equally named slog
.
Basic usage is just a simple string as a message:
(slog/info "my message")
#=> stdout: time="2023-07-07T18:50:54Z" level="info" msg="my message"
More context can be added by just sending more parameters:
(slog/debug "failed login" :user_id 1)
#=> stdout: time="2023-07-07T18:50:54Z" level="debug" msg="failed login" user_id="1"
The number of parameters after the message have to be even. This won't work:
(slog/error "this won't work" :a 1 :b)
#=> error: slog: arguments have odd number of elements.
The default logger prints out to stdout
with a text formatter. That behaviour
can be changed by creating your own logger, with slog/new
, which takes a
file or buffer as its first argument. This returns a function which takes an
even number of arguments and prints them out to the given buffer.
(def mylogger (slog/new (file/open "foo.log" :a)))
(mylogger :level :debug :msg "custom")
(mylogger :level :info :msg "custom")
#=> $ cat foo.log
# time="2023-07-07T19:00:42Z" level="debug" msg="custom"
# time="2023-07-07T19:00:42Z" level="info" msg="custom"
You can make this the default logger:
(slog/set-default mylogger)
(slog/debug "another one")
#=> $ cat foo.log
# time="2023-07-07T19:00:42Z" level="debug" msg="custom"
# time="2023-07-07T19:00:42Z" level="info" msg="custom"
# time="2023-07-07T19:02:13Z" level="debug" msg="another one"
Output can be changed to JSON by passing a formatter, which is a function which takes an even-numbered tuple and returns a string:
(defn format-json [tuple] (json/encode (struct ;tuple)))
(def jsonlogger (slog/new stdout :formatter format-json))
(jsonlogger :level :debug :msg "message")
#=> stdout: {"time":"2023-07-07T19:02:13Z","msg":"message","level":"debug"}
# make this the default logger
(slog/set-default jsonlogger)
By default time is time in UTC formatted in ISO 8601. That can also be changed, by passing a function which takes no arguments and returns a string:
(defn mytimer [] "always now")
(def mylogger (slog/new stdout :timer mytimer))
(mylogger :level :debug :msg "another one")
#=> stdout: time="always now" level="debug" msg="another one"
You can define the minimal log level, so only messages at or above that level get printed:
(slog/set-level :error)
(slog/debug "won't print")
(slog/info "won't print")
(slog/error "will print")
(slog/fatal "will print")
(slog/unknown "will print")
Contributions are welcome! The whole code is only 60-ish lines, easy to read and add improvements.