# 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).

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

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

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

```diff
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).

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

```clojure
(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.

```bash
❯ 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.
