Clojure Ring Logging
Published: 2023-11-26 17:34:31

---
title: Clojure Ring Logging
published: true
date: 2023-08-12 11:39:30 UTC
tags: 
canonical_url: https://blog.tvaisanen.com/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](https://github.com/seancorfield/clj-new).

```
clojure -Tclj-new app :name acme/app

```

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

```

Next, set up the [clojure tools logging](https://github.com/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"]}

```

Create a file `resources/log4j2.properties` and save the file with the following content. Read more about the configuration options from the [here](https://logging.apache.org/log4j/2.x/manual/configuration.html#Properties).

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

```

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!))

```

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}

```

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

- [https://github.com/ring-clojure/ring](https://github.com/ring-clojure/ring)

- [https://github.com/nberger/ring-logger](https://github.com/nberger/ring-logger)

Thanks for reading, hope you found this helpful.