Postmortem: A tiny value-oriented debugging tool, powered by transducers

I’ve just published the first release of Postmortem, a tiny debugging library for Clojure(Script)!

Postmortem is heavily inspired by similar existing libraries like scope-capture and miracle.save, but focuses more on the ease of “postmortem debugging”, as its name suggests.

One notable feature is integration with transducers. Using transducers, you can freely specify when to log data and/or what part of data will be logged:

(require '[postmortem.core :as pm]
         '[postmortem.xforms :as xf])

(defn sum [n]
  (loop [i n sum 0]
    (pm/dump :sum (xf/take-last 5))
    (if (= i 0)
      sum
      (recur (dec i) (+ i sum)))))

(sum 100) ;=> 5050

(pm/log-for :sum)
;=> [{:n 100, :i 4, :sum 5040}
;    {:n 100, :i 3, :sum 5044}
;    {:n 100, :i 2, :sum 5047}
;    {:n 100, :i 1, :sum 5049}
;    {:n 100, :i 0, :sum 5050}]

Another interesting feature is instrumentation. Like clojure.spec’s instrumentation, you can enable/disable logging for a function without touching its code at all. Instrumentation is useful especially for debugging a recursive function (and transducer integration also gives even more power to the feature):

(require '[postmortem.instrument :as pi])

(defn broken-factorial [n]
  (cond (= n 0) 1
        (= n 7) (/ (broken-factorial (dec n)) 0) ;; <- BUG HERE!!
        :else (* n (broken-factorial (dec n)))))

(pi/instrument `broken-factorial
               {:xform (comp (xf/take-until :err) (xf/take-last 5))})

(broken-factorial 10)
;; Execution error (ArithmeticException) at user/broken-factorial.
;; Divide by zero

(pm/log-for `broken-factorial)
;=> [{:args (3), :ret 6}
;    {:args (4), :ret 24}
;    {:args (5), :ret 120}
;    {:args (6), :ret 720}
;    {:args (7), :err #error {:cause "Divide by zero" ...}}]

If you’re interested, give it a try! Any feedback is welcome. :wink:

4 Likes