exupero's blog
RSSApps

Logging macros

In the previous post I showed a few debugging macros, but most of the time I simply use print statements. A quick way to print a value with minimal disruption to the surrounding code is to wrap it in doto, which prints the value without changing what's returned:

(let [a 1]
  (doto a prn))
1
1

When printing several different values at the same time it can become tricky to distinguish what code produced what value. Using macros, however, we can log the original form with its output:

(defmacro spy [x]
  `(let [x# ~x]
     (println
       (pr-str '~x)
       "=>"
       (pr-str x#))
     x#))
(let [a 1
      b 2]
  (spy (+ a b)))
(+ a b) => 3
3

If you're working in a terminal that uses ANSI colors, you can make the source form a different color from the value:

(defmacro spy-with-ansi-color [x]
  `(let [x# ~x]
     (println
       "\u001b[38;5;35m"
       (pr-str '~x)
       "\n\u001b[38;5;15m"
       (pr-str x#))
     x#))

If you're using ClojureScript, you can use CSS colors:

(defmacro spy-cljs [x]
  `(let [x# ~x]
     (js/console.log
       "%c%s\n%c%s"
       "color:mediumseagreen"
       (pr-str '~x)
       "color:black"
       (pr-str x#))
     x#))

You can also avoid the extra set of parentheses by invoking a logging macro with a data reader. Just add an entry to data_readers.clj that maps to a macro like this:

(defmacro spyp [x]
  `(spy ~x))
(let [a 1
      b 2]
  #spy/p (+ a b))
(+ a b) => 3
3

The name spy/p is taken from the spyscope library, which is worth checking out if you do a lot of print-based debugging.