git clone ''

(ql:quickload :pyr.unilog)

unilog: logging should be easy!

Build Status 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 allow external configuration.

Unilog provides a simple and somewhat opiniated way of configuring logback through simple clojure maps.


[spootnik/unilog "0.7.24"]


Let's pretend you have an application, which reads its initial configuration in a YAML file:

  foo: bar
  level: info
  console: true
    - "/var/log/program.log"
    - file: "/var/log/program-json.log"
      encoder: json
    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)

## Configuration details

The configuration, given as a map to `start-logging!` understands
a number of keys.

### Global Options

* `:level`: Default logging level
  * any of `:all`, `:trace`, `:debug`, `:info`, `:warn`, `:error`, `:off`
* `:external`
  * If it is `true`, do not try to configure logging. An external configuration is supplied.
* `:overrides`
  * Provide a map of namespace to level, overriding the provided default level.

### Console

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}}`.

### File

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}}`

### Files

Expects a sequence of valid configurations for `File`.

### Appenders

As for `Files`, but do not assume a specific appender, expect it to be supplied in the configuration map.

## Example 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:

:console appender

{:appender :console}

{:appender :console
 :encoder  :pattern}

{:appender :console
 :encoder  :pattern
 :pattern  "%p [%d] %t - %c %m%n"}

{:appender :console
 :encoder  :json}

:file appender

{: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}

:rolling-file appender

There are two rolling policies.

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
                  ;; for elaborate description of :max-size
                  :max-size    51200 ; bytes
                  ;; :pattern combines with :file
                  ;; The rolling period is defined by :pattern.
                  ;; Refer to
                  :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
                     :max-size 51200}} ; 51200 bytes

:socket appender

{:appender            :socket
 :remote-host        "localhost"
 :port                2004
 :queue-size          500
 :reconnection-delay "10 seconds"
 :event-delay-limit  "10 seconds"}

:syslog appender

{: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.

API documentation

Full API documentation is available at










Copyright © 2014 Pierre-Yves Ritschard MIT/ISC License, See LICENSE file.