clojure.tools.logging is a great library to perform logging. It walks through several available options such as slf4j, commons-logging, log4j, and logback.
While the logging itself is simple and straightforward, navigating the many ways to configure logging can be a bit daunting since the above logging frameworks which clojure.tools.logging allow external configuration.
Unilog provides a simple and somewhat opiniated way of configuring logback through simple clojure maps.
[spootnik/unilog "0.7.13"]
Let's pretend you have an application, which reads its initial configuration in a YAML file:
other-config:
foo: bar
logging:
level: info
console: true
files:
- "/var/log/program.log"
- file: "/var/log/program-json.log"
encoder: json
overrides:
some.namespace: debug
You would supply configuration by parsing the YAML and then
calling start-logging!
(require '[clj-yaml.core :refer [parse-string]]
'[unilog.config :refer [start-logging!]])
(let [default-logging {:level "info" :console true}
config (parse-string (slurp "my-config.yml"))]
(start-logging! (merge default-logging (:logging config)))
;; rest of program startup)
The configuration, given as a map to start-logging!
understands
a number of keys.
:level
: Default logging level- any of
:all
,:trace
,:debug
,:info
,:warn
,:error
,:off
- any of
:external
- If it is
true
, do not try to configure logging. An external configuration is supplied.
- If it is
:overrides
- Provide a map of namespace to level, overriding the provided default level.
If the :console
key is present in the configuration map, it may be any of:
false
- Do not log to the console.
true
- Log to the console, using a pattern encoder and the default pattern.
- A string
- Log to the console, using a pattern encoder and the supplied pattern string.
- A map
- Log to the console, other attributes are taken from the map.
- For instance:
{:console {:encoder :json}}
.
If the :file
key is present in the configuration map, it may be any of:
- A string: Log to the provided file, using a pattern encoder and the default pattern.
- A map: Log to a file, taking configuration attributes from the map.
- For instance:
{:file {:file "/var/log/foo.log" :encoder :json}}
- For instance:
Expects a sequence of valid configurations for File
.
As for Files
, but do not assume a specific appender, expect it to be supplied in the configuration map.
{:level :info
:console false
:files ["/var/log/standard.log"
{:file "/var/log/standard-json.log" :encoder :json}]
:file {:file "/var/log/file.log" :encoder :json}
:appenders [{:appender :file
:encoder :json
:file "/var/log/other-json.log"}
{:appender :file
:encoder :pattern
:pattern "%p [%d] %t - %c %m%n"
:file "/var/log/other-pattern.log"}
{:appender :rolling-file
:file "/var/log/rolling-file.log"}
{:appender :rolling-file
:rolling-policy :fixed-window
:triggering-policy :size-based
:file "/var/log/rolling-file.log"}
{:appender :rolling-file
:rolling-policy {:type :fixed-window
:max-index 5}
:triggering-policy {:type :size-based
:max-size 5120}
:file "/var/log/rolling-file.log"}]
:overrides {"org.apache.http" :debug
"org.apache.http.wire" :error}}
You could specify encoder arguments in some appenders. Not every appender supports encoders.
The following encoders are currently supported in :appenders
.
PatternLayoutEncoder
uses a default pattern of "%p [%d] %t - %c %m%n"
.
{:appender :file
:file "/var/log/file.log"
;; PatternLayoutEncoder
;; Without :pattern argument in an appender config, the default pattern is used.
:encoder :pattern}
{:appender :file
:file "/var/log/file2.log"
:encoder :pattern
:pattern "%p [%d] %t - %c %m%n"}
LogstashEncoder
formats messages for logstash.
{:appender :file
:file "/var/log/file3.log"
;; LogstashEncoder
:encoder :json}
The following appenders are currently supported:
- Optional Arguments
:encoder
:pattern
{:appender :console}
{:appender :console
:encoder :pattern}
{:appender :console
:encoder :pattern
:pattern "%p [%d] %t - %c %m%n"}
{:appender :console
:encoder :json}
- Mandatory Arguments
:file
- Optional Arguments
:encoder
:pattern
{:appender :file
:file "/var/log/file.log"}
{:appender :file
:file "/var/log/file.log"
:encoder :pattern}
{:appender :file
:file "/var/log/file.log"
:encoder :pattern
:pattern "%p [%d] %t - %c %m%n"}
{:appender :file
:file "/var/log/file.log"
:encoder :json}
- Mandatory Arguments
:file
- Optional Arguments
:rolling-policy
:triggering-policy
:encoder
:pattern
There are two rolling policies.
:fixed-window
- Renames files according to a fixed window algorithm.
:time-based
- Defines a rollover based on time.
Don't use a triggering policy with :time-based
rolling policy since :time-based
rolling policy is its own triggering policy as well.
You can specify a rolling policy by the keyword.
{:appender :rolling-file
:rolling-policy :fixed-window
:file "/var/log/rolling-file.log"
:encoder :pattern}
{:appender :rolling-file
:rolling-policy :time-based
:file "/var/log/rolling-file2.log"
:encoder :pattern
:pattern "%p [%d] %t - %c %m%n"}
If you want to specify arguments for a rolling policy, you can pass a map to :rolling-policy
as below. every argument to a rolling policy except :type
is optional.
{:appender :rolling-file
:file "rolling-file.log"
:rolling-policy {:type :fixed-window
:min-index 1
:max-index 5
;; :pattern combines with :file to make the name of a rolled log file.
;; For example, "rolling-file.log.%i.gz"
;; %i is index.
:pattern ".%i.gz"}
:encoder :json}
{:appender :rolling-file
:file "rolling-file2.log"
;; If you use this rolling policy, don't use a triggering policy
:rolling-policy {:type :time-based
;; log files are kept for :max-history periods.
;; periods can be hours, days, months, and so on.
:max-history 5
;; Before a period ends, if a log file reaches :max-size, it is rolled.
;; :max-size adds %i to :pattern. Without :max-size, you shouldn't
;; specify %i in :pattern.
;; Refer to http://logback.qos.ch/manual/appenders.html#SizeAndTimeBasedFNATP
;; for elaborate description of :max-size
:max-size 51200 ; bytes
;; :pattern combines with :file
;; The rolling period is defined by :pattern.
;; Refer to http://logback.qos.ch/manual/appenders.html#tbrpFileNamePattern
:pattern ".%d{yyyy-MM-dd}.%i"}
:encoder :pattern
:pattern "%p [%d] %t - %c %m%n"}
There is only one triggering policy, :size-based
.
{:appender :rolling-file
:rolling-policy :fixed-window
;; If you don't pass any argument to :size-based triggering policy, it triggers a rollover
;; when a log file grow beyond SizeBasedTriggeringPolicy/DEFAULT_MAX_FILE_SIZE.
:triggering-policy :size-based
:file "rolling-file.log"}
{:appender :rolling-file
:rolling-policy :fixed-window
:triggering-policy {:type :size-based
;; Refer to
;; http://logback.qos.ch/manual/appenders.html#SizeBasedTriggeringPolicy
:max-size 51200}} ; 51200 bytes
- Optional Arguments
:remote-host
:port
:queue-size
:reconnection-delay
:event-delay-limit
{:appender :socket
:remote-host "localhost"
:port 2004
:queue-size 500
:reconnection-delay "10 seconds"
:event-delay-limit "10 seconds"}
- Optional Arguments
:host
:port
{:appender :syslog
:host "localhost"
:port 514}
If you wish to supply your own configuration functions for appenders or encoders, you may do so by
adding multi-methods for build-appender
and build-encoder
. build-appender
dispatches
on the :appender
key in a configuration map while build-encoder
dispatches on the :encoder
key.
These functions receive the provided configuration map and may thus expect specific keys to be present to perform their configuration.
You may need to add a multimethod for start-appender!
if your appender needs a specialized initialization procedure.
Full API documentation is available at http://pyr.github.io/unilog
Copyright © 2014 Pierre-Yves Ritschard [email protected] MIT/ISC License, See LICENSE file.