nberger / ring-logger

Log ring requests & responses using your favorite logging backend
Eclipse Public License 1.0
101 stars 21 forks source link
clojure http logging ring ring-middleware

Ring-logger Circle CI

Ring middleware to log response time and other details of each request that arrives to your server.

DOCUMENTATION | 0.7.x

Clojars Project

Getting started

Add the dependency to your project.

Leiningen:

[ring-logger "1.1.1"]

deps.edn:

ring-logger/ring-logger {:mvn/version "1.1.1"}

Usage

Add the middleware to your ring stack:

    (ns foo
      (:require [ring.adapter.jetty :as jetty]
                [ring.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})

Example output:

INFO  ring.logger: {:request-method :get, :uri "/", :server-name "localhost", :ring.logger/type :starting}
DEBUG ring.logger: {:request-method :get, :uri "/", :server-name "localhost", :ring.logger/type :params, :params {:name "ring-logger", :password "[REDACTED]"}}
INFO  ring.logger: {:request-method :get, :uri "/", :server-name "localhost", :ring.logger/type :finish, :status 200, :ring.logger/ms 11}

Advanced usage

ring.logger comes with more fine-grained middleware apart from wrap-with-logger:

To log just the start and finish of requests (no parameters):

    (-> handler
        logger/wrap-log-response
        ;; more middleware to parse params, cookies, etc.
        logger/wrap-log-request-start)

To measure request latency, wrap-log-response will use the ring.logger/start-ms key added by wrap-log-request-start if both middlewares are being used, or will call System/currentTimeMillis to obtain the value by itself.


To explicitly log start, finish, and parameters of requests:

    (-> (handler)
        wrap-log-response
        ;; the following line must come before `wrap-params`
        (wrap-log-request-params {:transform-fn #(assoc % :level :info)})
        wrap-keyword-params        ;; optional
        wrap-params                ;; required
        wrap-log-request-start)

Note that in the above example, the :transform-fn is optional. You can either bump your log level to DEBUG or use :transform-fn to adjust the log level of params logging before it hits the log-fn. The latter is often preferable, since DEBUG tends to be very noisy.

Also see the example.

Using other logging backends

Other logging backends can be plugged by passing the log-fn option. This is how you could use timbre instead of c.t.logging:

    (require '[timbre.core :as timbre])

    (-> handler
        (logger/wrap-log-response {:log-fn (fn [{:keys [level throwable message]}]
                                             (timbre/log level throwable message))}))

What gets logged

All messages will be usually timestamped by your logging infrastructure.

How to disable exceptions logging

This is especially useful when also using ring.middleware.stacktrace.

(wrap-with-logger app {:log-exceptions? false})

Password: "[REDACTED]"

Sensitive information in params and headers can be redacted before it's sent to the logs.

This is very important: Nobody wants user passwords or authentication tokens to get to the logs and live there forever, in plain text, right?

By default, ring-logger will redact any parameter whose name is in the ring.logger/default-redact-key? set (:password, :token, :secret, etc). You can pass your own set or function to determine which keys to redact as the redact-key? option

(wrap-with-logger app {:redact-key? #{:senha :token})

Logging only certain requests

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

(:require [ring.middleware.conditional :as c :refer  [if-url-starts-with
                                                      if-url-doesnt-start-with
                                                      if-url-matches
                                                      if-url-doesnt-match]])

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

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

       wrap-with-other-handler))

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

Similar projects

pjlegato/ring.middleware.logger: ring-logger started as a fork of ring.middleware.logger. It's a great option if you don't mind pulling a transitive dependency on onelog & log4j.

lambdaisland/ring.middleware.logger: a fork of r.m.logger that replaces onelog with clojure.tools.logging

Contributing

Pull requests are welcome!

License

Copyright (C) 2015-2018 Nicolás Berger Copyright (C) 2012-2014 Paul Legato.

Distributed under the Eclipse Public License, the same as Clojure.