Skip to content

Instantly share code, notes, and snippets.

@rarous
Last active December 10, 2017 06:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rarous/2afcf6d6b4106c74aadc199b03dce771 to your computer and use it in GitHub Desktop.
Save rarous/2afcf6d6b4106c74aadc199b03dce771 to your computer and use it in GitHub Desktop.
Structured logging adapter for ring-logger middleware
(ns ring.logger.cambium
(:require
[cambium.core :as log]
[ring.logger.messages :as messages]
[ring.logger.protocols :refer [Logger]]
[ring.logger :as logger]))
(defrecord CambiumLogger []
Logger
(add-extra-middleware [_ handler] handler)
(log [_ level throwable message]
(if throwable
(log/log level (ex-data throwable) throwable message)
(log/log level message))))
(defn make-cambium-logger []
(CambiumLogger.))
(defn wrap-with-logger
"Returns a Ring middleware handler which uses Cambium as logger.
Supported options are the same as of ring.logger/wrap-with-logger, except of
:logger-impl which is fixed to a CambiumLogger instance"
([handler options]
(logger/wrap-with-logger
handler
(merge options {:logger (make-cambium-logger)
:printer :structured})))
([handler] (wrap-with-logger handler {})))
(defn wrap-with-body-logger
"Returns a Ring middleware handler which logs request body payloads using
Cambium as logger."
[handler]
(logger/wrap-with-body-logger handler (make-cambium-logger)))
(defn- redact-map [m {:keys [redact-fn]}]
(if redact-fn (redact-fn m) m))
(defn- make-structured-starting-message
[options {:keys [request-method uri remote-addr query-string headers]}]
{:method request-method
:uri (str uri (if query-string (str "?" query-string)))
:remote-addr remote-addr
:headers (redact-map headers options)})
(defmethod messages/starting :structured
[options req]
(log/info (make-structured-starting-message options req) "Starting request"))
(defmethod messages/request-details :structured
[_ req]
(log/debug
(select-keys req [:character-encoding
:content-length
:content-type
:query-string
:remote-addr
:request-method
:scheme
:server-name
:server-port
:uri])
"Request details"))
(defmethod messages/request-params :structured
[options {:keys [params]}]
(when params
(log/info (redact-map params options) "Params")))
(defmethod messages/sending-response :structured
[options response]
(log/trace
(cond-> response
(:cookies response) (update-in [:cookies] keys)
(:headers response) (update-in [:headers] #(redact-map % options)))
"Sending response"))
(defn- make-and-log-finished-message
[{:keys [timing]}
{:keys [request-method uri remote-addr query-string]}
{:keys [status] :as resp}
title
time]
(let [log-message
{:method request-method
:uri (str uri (if query-string (str "?" query-string)))
:remote-addr remote-addr
:status status}
log-message (if timing
(assoc log-message :duration time)
log-message)
log-message (if (= status 302)
(assoc log-message :redirect (get-in resp [:headers "Location"]))
log-message)]
(if (and (number? status) (>= status 500))
(log/error log-message "Error")
(log/info log-message title))))
(defn- get-total-time [{:keys [logger-start-time logger-end-time] :as req}]
(- logger-end-time logger-start-time))
(defmethod messages/finished :structured
[{:keys [timing] :as options} req resp]
(make-and-log-finished-message
options
req
resp
"Finished"
(when timing (get-total-time req))))
(defn- redact-request [req options]
(let [redact #(redact-map % options)]
(-> req
(update-in [:headers] redact)
(update-in [:params] redact)
;;take the keys from :form-params, because they might be nested
;;and we can't redact them in that case
(update-in [:form-params] keys))))
(defmethod messages/exception :structured
[{:keys [timing] :as options}
{:keys [request-method uri remote-addr] :as request}
throwable]
(let [message {:method request-method
:uri uri
:remote-addr remote-addr
:request (redact-request request options)}
message (if timing
(assoc message :duration (get-total-time request))
message)]
(log/error message throwable "Uncaught exception")))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment