DEV Community

Toni
Toni

Posted on • Originally published at blog.tvaisanen.com on

Clojure Ring Logging

A quick tutorial on how to set up request logging for a Clojure web app backend when using Ring and Jetty adapter.

First, create a new project with clj-new.

clojure -Tclj-new app :name acme/app

Enter fullscreen mode Exit fullscreen mode

And then add the Ring dependencies to the deps.edn file.

< :deps {org.clojure/clojure {:mvn/version "1.11.1"}}
---
> :deps {org.clojure/clojure {:mvn/version "1.11.1"}
> ring/ring-core {:mvn/version "1.6.3"}
> ring/ring-jetty-adapter {:mvn/version "1.6.3"}
> ring-logger/ring-logger {:mvn/version "1.1.1"}}

Enter fullscreen mode Exit fullscreen mode

Next, set up the clojure tools logging with log4j2 logging backend.

Update the deps.edn file by adding clj-log4j2 dependency and set:jvm-opts

5c5,6
< ring-logger/ring-logger {:mvn/version "1.1.1"}}
---
> ring-logger/ring-logger {:mvn/version "1.1.1"}
> clj-log4j2/clj-log4j2 {:mvn/version "0.4.0"}}
7c8,9
< {:run-m {:main-opts ["-m" "acme.app"]}
---
> {:run-m {:main-opts ["-m" "acme.app"]
> :jvm-opts ["-Dclojure.tools.logging.factory=clojure.tools.logging.impl/log4j2-factory"]}

Enter fullscreen mode Exit fullscreen mode

Create a file resources/log4j2.properties and save the file with the following content. Read more about the configuration options from the here.

status = warn
monitorInterval = 5

appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %date %level:%logger: %message%n%throwable

rootLogger.level = debug
rootLogger.appenderRef.stdout.ref = STDOUT

Enter fullscreen mode Exit fullscreen mode

Next wrap the ring handler with the wrap-with-logger middleware to log the requests. Configuration instructions can be found from the library github page.

(ns acme.app
  (:require [clojure.tools.logging :as logging]
            [ring.middleware.params]
            [ring.adapter.jetty :as jetty]
            [ring.logger :as logger])
  (:gen-class))

(def port 3000)

(defn ring-handler [_request]
  (logging/info "Info message")
  (logging/debug "Debug message")
  (logging/warn "Warn message")
  {:status 200
   :body "OK"})

(defonce server (atom nil))

(def app
  (-> #'ring-handler
      logger/wrap-with-logger))

(defn start! []
  (logging/info "Listening on port: " port)
  (reset! server
          (jetty/run-jetty #'app {:port port})))

(defn -main []
  (start!))

Enter fullscreen mode Exit fullscreen mode

Now you are ready to run the server with clj -M:run-m and you should see something like this in your console when the API is called with a POST request.

 clj -M:run-m
2023-08-12 14:23:50.358:INFO::main: Logging initialized @1171ms
2023-08-12 14:23:50,440 INFO:acme.app: Server starting with arguments: {}
2023-08-12 14:23:50.452:INFO:oejs.Server:main: jetty-9.2.21.v20170120
2023-08-12 14:23:50.472:INFO:oejs.ServerConnector:main: Started ServerConnector@4602f874{HTTP/1.1}{0.0.0.0:3000}
2023-08-12 14:23:50.472:INFO:oejs.Server:main: Started @1284ms
2023-08-12 14:23:51,716 INFO:ring.logger: {:request-method :post, :uri "/", :server-name "localhost", :ring.logger/type :starting}
2023-08-12 14:23:51,717 INFO:acme.app: Info message
2023-08-12 14:23:51,717 WARN:acme.app: Warn message
2023-08-12 14:23:51,717 INFO:ring.logger: {:request-method :post, :uri "/", :server-name "localhost", :ring.logger/type :finish, :status 200, :ring.logger/ms 1}

Enter fullscreen mode Exit fullscreen mode

That's about it for the minimal setup. Read more about advanced configuration from the sources.

Thanks for reading, hope you found this helpful.

Top comments (0)