Ring middleware to log each request
Clone or download
Latest commit e12a04e Dec 8, 2014
Type Name Latest commit message Commit time
Failed to load latest commit information.
src/ring/middleware Update logger.clj Dec 7, 2014
.gitignore Ignore logs/ dir May 23, 2012
README.md Remove reference to non-existent function. Oct 28, 2014
project.clj Bump version Jul 29, 2014



Ring middleware to log the duration and other details of each request.

The logging backend is pluggable, and defaults to clojure.tools.logging if none is given.

This is beta-level software. A number of people are using it in the wild. API changes are unlikely at this point. Bugs are possible. Pull requests are welcome!


In your project.clj, add the following dependency:

    [ring.middleware.logger "0.5.0"]

Then, just add the middleware to your stack. It comes preconfigured with reasonable defaults, which append ANSI colorized log messages on each request to whatever logger is in use by clojure.tools.logging.

    (ns foo
      (:require [ring.adapter.jetty     :as jetty]
                [ring.middleware.logger :as logger]))

    (defn my-ring-app [request]
         {:status 200
          :headers {"Content-Type" "text/html"}
          :body "Hello world!"})

    (jetty/run-jetty (logger/wrap-with-logger my-ring-app) {:port 8080})

Logging only certain requests

If you wish to restrict logging to certain paths (or other conditions), combine ring.middleware.logger with its companion project, ring.middleware.conditional, like so:

(:require [ring.middleware.conditional :as c :refer  [if-url-starts-with

(def my-ring-app
   (-> handler
       (if-url-starts-with "/foo" wrap-with-logger)

        ;; Or:
        ;; (c/if some-test-fn wrap-with-logger)
        ;; etc. 


Consult the ring.middleware.conditional docs for full details.

Custom Logger Backend

You can supply custom logger functions to wrap-with-logger by supplying pairs of :level custom-logger-fn as additional arguments. These will be used instead of the default clojure.tools.logging functions. The default mapping is:

      :info  (fn [x] (clojure.tools.logging/info x))
      :debug (fn [x] (clojure.tools.logging/debug x))
      :error (fn [x] (clojure.tools.logging/error x))
      :warn  (fn [x] (clojure.tools.logging/warn x))

Replace these functions with whatever logging facility you'd like to use. Each function should take a string and log it at that log level. For example, if you want to use a different function to log info and debug messages, you could call wrap-with-logger like this:

      (wrap-with-logger my-ring-app
        :info (fn [x] (my.custom.logging/info x))
        :debug (fn [x] (my.custom.logging/debug x)))

What Gets Logged

The default setup logs:

  • an :info-level message when a request begins;
  • an :info level message when a response is generated without any server errors (i.e. its HTTP status is < 500);
  • an :error level message when a response's HTTP status is >= 500;
  • an :error level message with a stack trace when an exception is thrown during response generation.

All messages are timestamped. Each request is assigned a random 4-hex-digit ID, so that different log messages pertaining to the same request can be cross-referenced. These IDs are printed in random ANSI colors by default, for easy visual correlation of log messages while reading a log file.

Log Levels

The logger logs at INFO level by default. More verbose information is logged when the logger is at DEBUG level.

Ring.middleware.logger uses OneLog internally, so we can use OneLog's convenience methods to change the log level:


Example Log

This is an example of logging at DEBUG level. af82 is the random ID assigned to this particular web request. The actual ID number output is ANSI-colorized for easy visual correlation of information related to a given request.

2014-09-25 01:46:47,328 (worker-1) [INFO] : (af82) Starting :get /favicon.ico for {"host" "localhost:8090", "user-agent" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9) AppleWebKit/___.__ (KHTML, like Gecko) Chrome/--.-.----.--- Safari/---.--", "cookie" "ring-session=12345678-1234-1234-1234-1234567890abc", "connection" "keep-alive", "if-modified-since" "Wed, 24 Sep 2014 02:21:59 +0000", "accept" "*/*", "accept-language" "en-US", "accept-encoding" "gzip,deflate,sdch", "dnt" "1"}
2014-09-25 01:46:47,328 (worker-1) [DEBUG] : (af82) Request details: {:character-encoding "utf8", :content-length 0, :request-method :get, :scheme :http, :query-string nil, :uri "/favicon.ico", :remote-addr "", :server-name "localhost", :server-port 8090}
limefog.log.2014-09-25:2014-09-25 01:46:47,330 (worker-1) [INFO] : (af82) Finished :get /favicon.ico for in (3 ms) Status: 304


Copyright (C) 2012-2014 Paul Legato. Distributed under the Eclipse Public License, the same as Clojure.