Attilah
Attilah

Reputation: 17932

Clojure Web App : How to step through requests

I have inherited a clojure app using the following components:

In order to gain an understanding of the app, I'd like to step through requests. I'm using Emacs as my IDE.

Is there any tool or techniques I can use to accomplish this ?

Upvotes: 1

Views: 136

Answers (1)

noisesmith
noisesmith

Reputation: 20194

Sadly, Clojure doesn't have any readily available step debugger. One can connect to the jvm with jdb and step through the bytecode, but this will not be a direct reflection of your Clojure code (especially thanks to things like laziness, potentially causing certain code to be evaluated from different contexts in the app than the source layout would lead you to expect).

All is not lost though. Because there is a strong focus on using immutible data and pure functions in idiomatic Clojure code, it is straightforward to capture the values of the inputs your functions get at runtime, in order to investigate and / or experiment with new values. For example:

inside your core namespace, you define your handler and launch jetty, and start an nrepl server from the same process:

(ns my-server.core
  (:require [ring.middleware
             [json :refer (wrap-json-params)]
             [multipart-params :refer (wrap-multipart-params)]]
...
             [clojure.tools.nrepl.server :as nrepl-server]))

...

(defn init
  []
  (when (= (System/getProperty "with_shell") "true")
    (nrepl-server/start-server :port 7888))
  (run-jetty handler :port 8080))

within a namespace with the code serving a particular request, you can keep track of incoming data in order to use it / investigate it in the repl

(ns my-ns.controllers.home)

(defonce debug (atom []))

(defn home
  [request]
  (let [in (java.util.Date.)
        response (process request)
        out (java.util.Date.)]
    (swap! debug conj {:in request :out response :fn home :timing [in out]})
    response))

Then, from the repl connection you can query the state of my-ns.controllers.hom/debug, by derefing the atom and seeing the various input and output values. This can be generalized to investigate the contents of various intermediate values, whereever you want to track execution. Since the data objects are more often than not immutible, you can have a full record to walk through if you create an atom to store the values. Note that by creating timestamps before and after calculating the request, you can profile the execution of the request handling function's body (which I have abstracted to a single function for clarity here).

There are also libraries like clojure.tools.trace if you want to use print based tracing.

Upvotes: 1

Related Questions