Liking cljdoc? Tell your friends :D

Debux

Debux is a simple but useful library for debugging Clojure and ClojureScript. I wrote this library to debug my own Clojure(Script) code and to analyze other developer’s one.

1. Prerequisites

  • clojure 1.8.0 or later

  • clojurescript 1.10.238 or later

2. Two libraries

In development, use the philoskim/debux library. When you use debux macros and functions from this library, it will emit debugging messages to the REPL window or the Chrome DevTools' console.

In production, use the philoskim/debux-stubs (https://github.com/philoskim/debux-stubs) library. This has the same public API as philoskim/debux but the macros simply expand only to the given original form itself.

With this setup

  • in production, your use of debux macros will have zero run-time and compile-time cost,

  • in development, debux macros are able to be turned off too via the set-debug-mode! function.

Never use philoskim/debux library in production because it will impose too much overhead on the peformance, especially in using dbgn and clogn, even though (set-debug-mode! false) is run.

3. Installation

First, please be sure to read the "Two libraries" section immediately above for background.

To include debux in your project for development, simply add the following to your project.clj development dependencies:

[philoskim/debux "0.6.4"]

and this to your production dependencies.

[philoskim/debux-stubs "0.6.4"]

4. Recent change logs

You can see All change logs since v0.3.0 here.
  • v0.6.4

    • Bug fixed: issue #16

    • Bumps clojure-future-spec 1.9.0 and cljs-devtools 1.0.0

  • v0.6.3

    • The printed format of evaluating the multiple forms in dbg/clog is changed for readability. See the detalis here.

  • v0.6.2

    • The namespace and line number of the source code are relocated to the top line for readability.

  • v0.6.1

    • The namespace and line number of the source code are printed as well in the debux macros.

  • v0.6.0

    • Changed: The number option of debux macros is now applied to not only seq data type but also all the coll data types. (Previously, the number option was applied to only seq data type.)

      • This change will prevent the elements of the deeply nested massive coll data types from being printed too much. See an example here

    • Renamed: set-print-seq-length! --> set-print-length!

  • v0.5.9

  • v0.5.8

    • The dbg-last macro added. See the detalis here.

  • v0.5.7

    • :print option added to dbg/clog. See the detalis here.

5. How to use

In Clojure, the following line should be included in your file.

(use 'debux.core)

In ClojureScript, the following (:require ...) line has to be included in your file.

examples/core.cljs
(ns examples.core
  (:require [debux.cs.core :as d :refer-macros [clog clogn dbg dbgn break]]))


Table 1. Debugging API use
dbgdbgnclogclognbreak

Clojure REPL

O

O

X

X

X

ClojureScript REPL

O

O

X

X

X

ClojureScript Browser console

O

O

O

O

O

  • Legend: O (supported), X (not supported)

  1. dbg/dbgn can be used in Clojure REPL.

  2. dbg/dbgn can be used in ClojureScript REPL like weasel or figwheel.

  3. dbg/dbgn , clog/clogn and break can be used in the browser console window like Chrome DevTools.

    I recommend that you should use clog/clogn instead of dbg/dbgn in the browser console window, because clog/clogn uses the console.log function of browser’s developer tools to style the form. You can see its effect here.

6. dbg examples

You can see every example source code of this document in examples folder.

6.1. Basic usage

The features of clog are almost the same as those of dbg.

The macro dbg prints an original form and pretty-prints the evaluated value on the REPL window. Then it returns the value without interrupting the code evaluation.

(* 2 (dbg (+ 10 20)))
; => 60
REPL output
{:ns examples.demo, :line 8}
dbg: (+ 10 20) =>
|   30

Sometimes you need to see multiple forms evaluated. To do so, a literal vector form can be used like this.

(defn my-fun
  [a {:keys [b c d] :or {d 10 b 20 c 30}} [e f g & h]]
  (dbg [a b c d e f g h]))

(my-fun (take 5 (range)) {:c 50 :d 100} ["a" "b" "c" "d" "e"])
; => [(0 1 2 3 4) 20 50 100 "a" "b" "c" ("d" "e")]
REPL output
{:ns examples.demo, :line 11}
dbg: [a b c d e f g h] =>
|   {:a (0 1 2 3 4),
|    :b 20,
|    :c 50,
|    :d 100,
|    :e "a",
|    :f "b",
|    :g "c",
|    :h ("d" "e")}
Notice that the printed value is a map, not an original vector and the form is prepended with a colon to distinguish the form from the evaluated value.

Further examples:

(def a 10)
(def b 20)

(dbg [a b [a b] :c])
; => [10 20 [10 20] :c]
REPL output
{:ns examples.demo, :line 15}
dbg: [a b [a b] :c] =>
|   {:a 10, :b 20, :[a b] [10 20], ::c :c}

In general, dbg prints the evaluated result of the outermost form except for the following four special cases(->, ->>, let, comp).

6.2. debugging thread macro -> or ->>

6.2.1. Using outside the thread macros

When debugging the thread-first macro -> or thread-last macro ->>, dbg prints every expression in the thread macros.

This is an example of thread-first macro ->.

(dbg (-> "a b c d"
         .toUpperCase
         (.replace "A" "X")
         (.split " ")
         first))
;=> "X"
REPL output
{:ns examples.demo, :line 18}
dbg: (-> "a b c d" .toUpperCase (.replace "A" "X") (.split " ") first) =>
| "a b c d" =>
|   "a b c d"
| .toUpperCase =>
|   "A B C D"
| (.replace "A" "X") =>
|   "X B C D"
| (.split " ") =>
|   ["X", "B", "C", "D"]
| first =>
|   "X"

Another example.

(def person
  {:name "Mark Volkmann"
   :address {:street "644 Glen Summit"
             :city "St. Charles"
             :state "Missouri"
             :zip 63304}
   :employer {:name "Object Computing, Inc."
              :address {:street "12140 Woodcrest Dr."
                        :city "Creve Coeur"
                        :state "Missouri"
                        :zip 63141}}})

(dbg (-> person :employer :address :city))
; => "Creve Coeur"
REPL output
{:ns examples.demo, :line 37}
dbg: (-> person :employer :address :city) =>
| person =>
|   {:name "Mark Volkmann",
|    :address
|    {:street "644 Glen Summit",
|     :city "St. Charles",
|     :state "Missouri",
|     :zip 63304},
|    :employer
|    {:name "Object Computing, Inc.",
|     :address
|     {:street "12140 Woodcrest Dr.",
|      :city "Creve Coeur",
|      :state "Missouri",
|      :zip 63141}}}
| :employer =>
|   {:name "Object Computing, Inc.",
|    :address
|    {:street "12140 Woodcrest Dr.",
|     :city "Creve Coeur",
|     :state "Missouri",
|     :zip 63141}}
| :address =>
|   {:street "12140 Woodcrest Dr.",
|    :city "Creve Coeur",
|    :state "Missouri",
|    :zip 63141}
| :city =>
|   "Creve Coeur"

This is an example of thread-last macro ->>.

(def c 5)

(dbg (->> c (+ 3) (/ 2) (- 1)))
; => 3/4
REPL output
{:ns examples.demo, :line 42}
dbg: (->> c (+ 3) (/ 2) (- 1)) =>
| c =>
|   5
| (+ 3) =>
|   8
| (/ 2) =>
|   1/4
| (- 1) =>
|   3/4

6.2.2. Using inside the thread macros

If you want to debug one of the expressions within the thread macro -> or ->>, don’t do it like this.

(-> {:a [1 2]}
    (dbg (get :a))
    (conj 3))
; => java.lang.IllegalArgumentException
;    Don't know how to create ISeq from: java.lang.Long

You will have some exception. Instead, do it like this.

(-> {:a [1 2]}
    (get :a)
    dbg
    (conj 3))
; => [1 2 3]
REPL output
{:ns examples.demo}
dbg: (get {:a [1 2]} :a) =>
|   [1 2]

Another example.

(->> [-1 0 1 2]
     (filter pos?)
     (map inc)
     dbg
     (map str))
; => ("2" "3")
REPL output
{:ns examples.demo}
dbg: (map inc (filter pos? [-1 0 1 2])) =>
|   (2 3)
In the above examples, dbg doesn’t get the :line number information from the Clojure compiler, so it is omitted. I don’t know why the Clojure compiler doesn’t provide the line number informaton through the code of (:line (meta &form)) inside the thread macros -> or ->> in this situation. If anyone knows about it, please let me know.

See more examples here.

6.3. Debugging let or comp form

When debugging let form,

(dbg (let [a (take 5 (range))
           {:keys [b c d] :or {d 10 b 20 c 30}} {:c 50 :d 100}
           [e f g & h] ["a" "b" "c" "d" "e"]]
        [a b c d e f g h]))
; => [(0 1 2 3 4) 20 50 100 "a" "b" "c" ("d" "e")]

each binding will be printed like this.

REPL output
{:ns examples.demo, :line 58}
dbg: (let [a (take 5 (range)) {:keys [b c d], :or {d 10, b 20, c 30}} {:c 5 ... =>
| a =>
|   (0 1 2 3 4)
| {:keys [b c d], :or {d 10, b 20, c 30}} =>
|   {:keys [20 50 100], :or {100 10, 20 20, 50 30}}
| [e f g & h] =>
|   ["a" "b" "c" & ("d" "e")]

When debugging comp form,

(def c (dbg (comp inc inc +)))

(c 10 20)
; => 32

the result of each function will be printed like this.

REPL output
{:ns examples.demo, :line 64}
dbg: (comp inc inc +) =>
| + =>
|   30
| inc =>
|   31
| inc =>
|   32

7. dbgn examples

The features of clogn are almost the same as those of dbgn.

The macro dbgn is for Clojure/CloujureScript REPL and the macro clogn is for ClojureScript browser console only. The appended n to these two macro names means Nested forms. You can debug every nested form without interrupting code evaluations. This feature is very useful, especially when you analyze other developer’s source code.

7.1. Simple example

(dbgn (defn foo [a b & [c]]
        (if c
          (* a b c)
          (* a b 100))))

(foo 2 3)
; => 600

(foo 2 3 10)
; => 60
REPL output
{:ns examples.demo, :line 72}
dbgn: (defn foo [a b & [c]] (if c (* a b c) (* a b 100))) =>

| c =>
|   nil
| a =>
|   2
| b =>
|   3
| (* a b 100) =>
|   600
| (if c (* a b c) (* a b 100)) =>
|   600

| c =>
|   10
| a =>
|   2
| b =>
|   3
| (* a b c) =>
|   60
| (if c (* a b c) (* a b 100)) =>
|   60

7.2. Detailed examples

  • dbgn/clogn don’t have any problem in handling functions.

  • dbgn/clogn, however, can have some problem in case of macros and special forms.

    • Some macros such as when don’t have any problem when used in dbgn/clogn.

    • Other macros such as defn which has a binding vector can have problem because they have binding symbols which must not be evaluated in dbgn/clogn macros. In case of special forms and those macros in clojure.core namespace, degn/clogn can handle them appropriately.

    • In some cases, Clojure developers can write their own macros which dbgn/clogn cannot handle appporiately. So I categorized those macros in clojure.core namespace as the following table and you can register your own macros according to the macro types in the table. I will explain it in How to register your own macros in using dbgn/clogn.

Table 2. Categorized 19 types of macros in dbgn/clogn
Macro typesMacros in clojure.core and special forms

:def-type

def defonce

:defn-type

defn defn-

:fn-type

fn fn*

:let-type

binding dotimes let when-first when-let when-some with-in-str with-local-vars with-open with-out-str with-redefs

:if-let-type

if-let if-some

:letfn-type

letfn

:loop-type

loop

:for-type

for doseq

:case-type

case

:skip-arg-1-type

set! with-precision

:skip-arg-2-type

as->

:skip-arg-1-2-type

:skip-arg-1-3-type

defmethod

:skip-arg-2-3-type

amap areduce

:skip-arg-1-2-3-type

:skip-all-args-type

declare defmacro defmulti defstruct extend extend-protocol extend-type import memfn new ns proxy proxy-super quote refer-clojure reify sync var

:skip-form-itself-type

catch definline definterface defprotocol defrecord deftype finally

:expand-type

.. -> ->> doto cond-> cond->> condp import some-> some->>

:dot-type

.

7.2.1. :def-type example

This type of macros have the first argument which must not be evaluated and can have optional doc-string argument.

(dbgn (def my-function "my-function doc string"
        (fn [x] (* x x x))))

(my-function 10)
; => 1000
REPL output
{:ns examples.demo, :line 85}
dbgn: (def my-function "my-function doc string" (fn [x] (* x x x))) =>
| (fn [x] (* x x x)) =>
|   #function[example.core/eval24554/result--24229--auto----24555]
| (def my-function "my-function doc string" (fn [x] (* x x x))) =>
|   #'example.core/my-function

| x =>
|   10
| (* x x x) =>
|   1000

7.2.2. :defn-type example

This type of macros have the binding vector argument which must not be evaluated and can have optional doc-string, attr-map, or prepost-map arguments.

(dbgn (defn add
        "add doc string"
        [a b]
        (+ a b)))

(add 10 20)
; => 30
REPL output
{:ns examples.demo, :line 92}
dbgn: (defn add "add doc string" [a b] (+ a b)) =>

| a =>
|   10
| b =>
|   20
| (+ a b) =>
|   30


You can debug multiple-arity functions as well.

(dbgn (defn my-add
        "my-add doc string"
        ([] 0)
        ([a] a)
        ([a b] (+ a b))
        ([a b & more] (apply + a b more))))

; The function body in this case doesn't have any symbol to evaluate,
; so no output will be printed.
(my-add)
; => 0

(my-add 10)
; => 10

(my-add 10 20)
; => 30

(my-add 10 20 30 40)
; => 100
REPL output
{:ns examples.demo, :line 100}
dbgn: (defn my-add "my-add doc string" ([] 0) ([a] a) ([a b] (+ a b)) ([a b  ... =>

| a =>
|   10

| a =>
|   10
| b =>
|   20
| (+ a b) =>
|   30

| + =>
|   #function[clojure.core/+]
| a =>
|   10
| b =>
|   20
| more =>
|   (30 40)
| (apply + a b more) =>
|   100


You can have multiple dbgn/clogns.

(dbgn (defn calc1 [a1 a2] (+ a1 a2)))
(dbgn (defn calc2 [s1 s2] (- 100 (calc1 s1 s2))))
(dbgn (defn calc3 [m1 m2] (* 10 (calc2 m1 m2))))

(calc3 2 5)
; => 760
REPL output
{:ns examples.demo, :line 113}
dbgn: (defn calc1 [a1 a2] (+ a1 a2)) =>

{:ns examples.demo, :line 114}
dbgn: (defn calc2 [s1 s2] (- 100 (calc1 s1 s2))) =>

{:ns examples.demo, :line 115}
dbgn: (defn calc3 [m1 m2] (* 10 (calc2 m1 m2))) =>

| m1 =>
|   2
| m2 =>
|   5

|| s1 =>
||   2
|| s2 =>
||   5

||| a1 =>
|||   2
||| a2 =>
|||   5
||| (+ a1 a2) =>
|||   7
|| (calc1 s1 s2) =>
||   7
|| (- 100 (calc1 s1 s2)) =>
||   93
| (calc2 m1 m2) =>
|   93
| (* 10 (calc2 m1 m2)) =>
|   930

7.2.3. :fn-type example

This type of macros have the binding vector argument which must not be evaluated and can have optional function name. So it is a little different from :defn-type macros.

(dbgn (reduce (fn [acc i] (+ acc i)) 0 [1 5 9]))
; => 15
REPL output
{:ns examples.demo, :line 121}
dbgn: (reduce (fn [acc i] (+ acc i)) 0 [1 5 9]) =>
| (fn [acc i] (+ acc i)) =>
|   #function[example.core/eval25034/result--24229--auto----25035]
| [1 5 9] =>
|   [1 5 9]

|| acc =>
||   0
|| i =>
||   1
|| (+ acc i) =>
||   1

|| acc =>
||   1
|| i =>
||   5
|| (+ acc i) =>
||   6

|| acc =>
||   6
|| i =>
||   9
|| (+ acc i) =>
||   15
| (reduce (fn [acc i] (clojure.core/binding [debux.common.util/*indent-l ... =>
|   15


Another example
(dbgn (map #(* % 10) [1 5 9]))
; => (10 50 90)
REPL output
{:ns examples.demo, :line 123}
dbgn: (map (fn* [p1__2514#] (* p1__2514# 10)) [1 5 9]) =>
| (fn* [p1__13193#] (try (clojure.core/reset! (:evals +debux-dbg-opts+)  ... =>
|   #object[example.dbgn$eval13194$result__4709__auto____13195 0x1b58788a "example.dbgn$eval13194$result__4709__auto____13195@1b58788a"]
| [1 5 9] =>
|   [1 5 9]

|| p1__13583# =>
||   1
|| (* p1__13583# 10) =>
||   10

|| p1__13583# =>
||   5
|| (* p1__13583# 10) =>
||   50

|| p1__13583# =>
||   9
|| (* p1__13583# 10) =>
||   90
| (map (fn* [p1__13583#] (clojure.core/binding [debux.common.util/*inden ... =>
|   (10 50 90)

7.2.4. :let-type example

This type of macros have the binding vector argument which must not be evaluated.

(dbgn (let [a (+ 1 2)
            [b c] [(+ a 10) (* a 2)]]
         (- (+ a b) c)))
; => 10
REPL output
{:ns examples.demo, :line 127}
dbgn: (let [a (+ 1 2) [b c] [(+ a 10) (* a 2)]] (- (+ a b) c)) =>
| (+ 1 2) =>
|   3
| a =>
|   3
| (+ a 10) =>
|   13
| (* a 2) =>
|   6
| [(+ a 10) (* a 2)] =>
|   [13 6]

| b =>
|   13
| (+ a b) =>
|   16
| c =>
|   6
| (- (+ a b) c) =>
|   10
| (let [a (+ 1 2) [b c] [(+ a 10) (* a 2)]] (- (+ a b) c)) =>
|   10

7.2.5. :if-let-type example

This type of macros are a little different from :let-type macros in that they need only one or two forms in their bodies.

(def a* 10)

(dbgn (if-let [s a*]
        (+ s 100)
        false))
; => 110
REPL output
{:ns examples.demo, :line 135}
dbgn: (if-let [s a*] (+ s 100) false) =>
| a* =>
|   10
| s =>
|   10
| (+ s 100) =>
|   110
| (if-let [s a*] (+ s 100) false) =>
|   110

7.2.6. :letfn-type example

This type of macro has the special binding vector syntax which is a bit different from :fn-type.

(dbgn (letfn [(twice [x]
                (* x 2))
              (six-times [y]
                (* (twice y) 3))]
        (six-times 15)))
; => 90
REPL output
{:ns examples.demo, :line 141}
dbgn: (letfn [(twice [x] (* x 2)) (six-times [y] (* (twice y) 3))] (six-time ... =>
| y =>
|   15
| x =>
|   15
| (* x 2) =>
|   30
| (twice y) =>
|   30
| (* (twice y) 3) =>
|   90
| (six-times 15) =>
|   90
| (letfn [(twice [x] (* x 2)) (six-times [y] (* (twice y) 3))] (six-time ... =>
|   90

7.2.7. :loop-type example

This type of macro is similiar to :let-type but has a significant difference because the recur has to be placed at the tail positon with the loop form. So it needs a special handling in the implementation of dbgn/clogn. Refer to Limited support for the form including recur for details.

7.2.8. :for-type example

This type of macros have a little different syntax from :let-type macros, because it can have :let, :when, or :while clause.

(dbgn (for [x [0 1 2 3 4 5]
            :let [y (* x 3)]
            :when (even? y)]
        y))
; => (0 6 12)
REPL output
{:ns examples.demo, :line 149}
dbgn: (for [x [0 1 2 3 4 5] :let [y (* x 3)] :when (even? y)] y) =>
| [0 1 2 3 4 5] =>
|   [0 1 2 3 4 5]
| x =>
|   0
| (* x 3) =>
|   0
| y =>
|   0
| (even? y) =>
|   true

| x =>
|   1
| (* x 3) =>
|   3
| y =>
|   3
| (even? y) =>
|   false

| x =>
|   2
| (* x 3) =>
|   6
| y =>
|   6
| (even? y) =>
|   true

| x =>
|   3
| (* x 3) =>
|   9
| y =>
|   9
| (even? y) =>
|   false

| x =>
|   4
| (* x 3) =>
|   12
| y =>
|   12
| (even? y) =>
|   true

| x =>
|   5
| (* x 3) =>
|   15
| y =>
|   15
| (even? y) =>
|   false
| (for [x [0 1 2 3 4 5] :let [y (* x 3)] :when (even? y)] (do (debux.com ... =>
|   (0 6 12)

7.2.9. :case-type example

This type of macro has the special syntax. Refer to the details here.

(dbgn (let [mystr "hello"]
        (case mystr
          "" 0
          "hello" (count mystr))))
; => 5
REPL output
{:ns examples.demo, :line 156}
dbgn: (let [mystr "hello"] (case mystr "" 0 "hello" (count mystr))) =>
| mystr =>
|   "hello"
| (count mystr) =>
|   5
| (case mystr "" 0 "hello" (count mystr)) =>
|   5
| (let [mystr "hello"] (case mystr "" 0 "hello" (count mystr))) =>
|   5


Another example
(dbgn (case 'a
        (x y z) "x, y, or z"
        "default"))
; => "default"
REPL output
{:ns examples.demo, :line 161}
dbgn: (case (quote a) (x y z) "x, y, or z" "default") =>
| (case (quote a) (x y z) "x, y, or z" "default") =>
|   "default"

7.2.10. :skip-arg-1-type example

This type of macros have the first argument which must not be evaluated. So dbgn/clogn internally skips the evaluation of this argument.

(dbgn (with-precision 10 (/ 1M 6)))
; => 0.1666666667M
REPL output
{:ns examples.demo, :line 167}
dbgn: (with-precision 10 (/ 1M 6)) =>
| (/ 1M 6) =>
|   0.1666666667M
| (with-precision 10 (/ 1M 6)) =>
|   0.1666666667M

7.2.11. :skip-arg-2-type example

This type of macros have the second argument which must not be evaluated. So dbgn/clogn internally skips the evaluation of this argument.

(dbgn (as-> 0 n
        (inc n)
        (inc n)))
; => 2
REPL output
{:ns examples.demo, :line 171}
dbgn: (as-> 0 n (inc n) (inc n)) =>
| n =>
|   0
| (inc n) =>
|   1
| n =>
|   1
| (inc n) =>
|   2
| (as-> 0 n (inc n) (inc n)) =>
|   2

7.2.12. :skip-arg-1-2-type example

This type of macros have the first and second arguments which must not be evaluated. So dbgn/clogn internally skips the evaluation of those arguments. However, I can’t find this type of macros in clojure.core namespace but add this type for completeness and the future possibilities of this type of macros.

7.2.13. :skip-arg-1-3-type example

This type of macros have the first and third arguments which must not be evaluated. So dbgn/clogn internally skips the evaluation of those arguments.

(defmulti greeting
  (fn [x] (:language x)))

(dbgn (defmethod greeting :english [map]
        (str "English greeting: " (:greeting map))))

(dbgn (defmethod greeting :french [map]
        (str "French greeting: " (:greeting map))))

(def english-map {:language :english :greeting "Hello!"})
(def french-map {:language :french :greeting "Bonjour!"})

(greeting english-map)
(greeting french-map)
REPL output
{:ns examples.demo, :line 180}
dbgn: (defmethod greeting :english [map] (str "English greeting: " (:greetin ... =>
| (defmethod greeting :english [map] (str "English greeting: " (:greetin ... =>
|   #object[clojure.lang.MultiFn 0x193bb809 "clojure.lang.MultiFn@193bb809"]

{:ns examples.demo, :line 183}
dbgn: (defmethod greeting :french [map] (str "French greeting: " (:greeting  ... =>
| (defmethod greeting :french [map] (str "French greeting: " (:greeting  ... =>
|   #object[clojure.lang.MultiFn 0x193bb809 "clojure.lang.MultiFn@193bb809"]

| map =>
|   {:language :english, :greeting "Hello!"}
| (:greeting map) =>
|   "Hello!"
| (str "English greeting: " (:greeting map)) =>
|   "English greeting: Hello!"

| map =>
|   {:language :french, :greeting "Bonjour!"}
| (:greeting map) =>
|   "Bonjour!"
| (str "French greeting: " (:greeting map)) =>
|   "French greeting: Bonjour!"

7.2.14. :skip-arg-2-3-type example

This type of macros have the second and third arguments which must not be evaluated. So dbgn/clogn internally skips the evaluation of those arguments.

(let [xs (float-array [1 2 3])]
  (dbgn (areduce xs i ret (float 0)
                 (+ ret (aget xs i)))))
; => 6.0
REPL output
{:ns examples.demo, :line 195}
dbgn: (areduce xs i ret (float 0) (+ ret (aget xs i))) =>
| xs =>
|   [1.0, 2.0, 3.0]
| (float 0) =>
|   0.0
| ret =>
|   0.0
| i =>
|   0
| (aget xs i) =>
|   1.0
| (+ ret (aget xs i)) =>
|   1.0
| ret =>
|   1.0
| i =>
|   1
| (aget xs i) =>
|   2.0
| (+ ret (aget xs i)) =>
|   3.0
| ret =>
|   3.0
| i =>
|   2
| (aget xs i) =>
|   3.0
| (+ ret (aget xs i)) =>
|   6.0
| (areduce xs i ret (float 0) (+ ret (aget xs i))) =>
|   6.0

7.2.15. :skip-arg-1-2-3-type example

This type of macros have the first, second and third arguments which must not be evaluated. So dbgn/clogn internally skips the evaluation of those arguments. However, I can’t find this type of macros in clojure.core namespace but add this type for completeness and the future possibilities of this type of macros.

7.2.16. :skip-all-args-type example

This type of macros ignores all the arguments and prints the outermost form and its result.

(dbgn (defmacro unless [pred a b]
        `(if (not ~pred) ~a ~b)))
REPL output
{:ns examples.demo, :line 200}
dbgn: (defmacro unless [pred a b] (clojure.core/seq (clojure.core/concat (cl ... =>
| (defmacro unless [pred a b] (clojure.core/seq (clojure.core/concat (cl ... =>
|   #'user/unless

7.2.17. :skip-form-itself-type example

This type of macros ignores the form itself and prints nothing.

(dbgn (try
        (/ 1 0)
        (catch ArithmeticException e (str "caught exception: " (.getMessage e)))))
REPL output
{:ns examples.demo, :line 205}
dbgn: (try (/ 1 0) (catch ArithmeticException e (str "caught exception: " (. ... =>
| (try (/ 1 0) (catch ArithmeticException e (str "caught exception: " (. ... =>
|   "caught exception: Divide by zero"
The evaluated resuts of the catch form are not printed in the above example.

7.2.18. :expand-type example

This type of macros will be expanded and then the output will be printed.

(dbgn (-> "a b c d"
          .toUpperCase
          (.replace "A" "X")
          (.split " ")
          first))
; => "X"
REPL output
{:ns examples.demo, :line 211}
dbgn: (-> "a b c d" .toUpperCase (.replace "A" "X") (.split " ") first) =>
| (.toUpperCase "a b c d") =>
|   "A B C D"
| (.replace (.toUpperCase "a b c d") "A" "X") =>
|   "X B C D"
| (.split (.replace (.toUpperCase "a b c d") "A" "X") " ") =>
|   ["X", "B", "C", "D"]
| (first (.split (.replace (.toUpperCase "a b c d") "A" "X") " ")) =>
|   "X"


Another example
(dbgn (.. "fooBAR"  toLowerCase  (contains "ooba")))
; => true
REPL output
{:ns examples.demo, :line 217}
dbgn: (.. "fooBAR" toLowerCase (contains "ooba")) =>
| (. "fooBAR" toLowerCase) =>
|   "foobar"
| (. (. "fooBAR" toLowerCase) (contains "ooba")) =>
|   true


Yet another example
(let [x 1 y 2]
  (dbgn (cond-> []
          (odd? x) (conj "x is odd")
          (zero? (rem y 3)) (conj "y is divisible by 3")
          (even? y) (conj "y is even"))))
; => ["x is odd" "y is even"]
REPL output
{:ns examples.demo, :line 220}
dbgn: (cond-> [] (odd? x) (conj "x is odd") (zero? (rem y 3)) (conj "y is di ... =>
| [] =>
|   []
| x =>
|   1
| (odd? x) =>
|   true
| G__14051 =>
|   []
| (conj G__14051 "x is odd") =>
|   ["x is odd"]
| (if (odd? x) (conj G__14051 "x is odd") G__14051) =>
|   ["x is odd"]
| y =>
|   2
| (rem y 3) =>
|   2
| (zero? (rem y 3)) =>
|   false
| G__14051 =>
|   ["x is odd"]
| (if (zero? (rem y 3)) (conj G__14051 "y is divisible by 3") G__14051) =>
|   ["x is odd"]

| (even? y) =>
|   true
| (conj G__14051 "y is even") =>
|   ["x is odd" "y is even"]
| (if (even? y) (conj G__14051 "y is even") G__14051) =>
|   ["x is odd" "y is even"]
| (clojure.core/let [G__14051 [] G__14051 (if (odd? x) (conj G__14051 "x ... =>
|   ["x is odd" "y is even"]

7.2.19. :dot-type example

(dbgn (. (java.util.Date.) getMonth))
; => 5
REPL output
{:ns examples.demo, :line 227}
dbgn: (. (java.util.Date.) getMonth) =>
| (java.util.Date.) =>
|   #inst "2017-06-27T08:04:46.480-00:00"
| (. (java.util.Date.) getMonth) =>
|   5

7.3. Limited support for the form including recur

Table 3. The forms including recur
dbgnclogn

loop ~ recur

O

O

defn/defn-/fn ~ recur

  • Legend: O (supported), (limitedly supported)

7.3.1. loop ~ recur

You can see the evaluated results of the form which incldues loop ~ recur by using dbgn in Clojure and ClojureScript.

(dbgn (loop [acc 1 n 3]
        (if (zero? n)
          acc
          (recur (* acc n) (dec n)))))
; => 6
REPL output
{:ns examples.demo, :line 233}
dbgn: (loop [acc 1 n 3] (if (zero? n) acc (recur (* acc n) (dec n)))) =>

| n =>
|   3
| (zero? n) =>
|   false
| acc =>
|   1
| (* acc n) =>
|   3
| (dec n) =>
|   2

| n =>
|   2
| acc =>
|   3
| (* acc n) =>
|   6
| (dec n) =>
|   1

| n =>
|   1
| acc =>
|   6
| (dec n) =>
|   0

| n =>
|   0
| (zero? n) =>
|   true
| (loop [acc 1 n 3] (debux.common.util/insert-blank-line) (if (zero? n)  ... =>
|   6


Another example
(dbgn (defn fact [num]
        (loop [acc 1 n num]
          (if (zero? n)
            acc
            (recur (* acc n) (dec n))))))

(fact 3)
REPL output
{:ns examples.demo, :line 239}
dbgn: (defn fact [num] (loop [acc 1 n num] (if (zero? n) acc (recur (* acc n ... =>

| num =>
|   3

| n =>
|   3
| (zero? n) =>
|   false
| acc =>
|   1
| (* acc n) =>
|   3
| (dec n) =>
|   2

| n =>
|   2
| acc =>
|   3
| (* acc n) =>
|   6
| (dec n) =>
|   1

| n =>
|   1
| acc =>
|   6
| (dec n) =>
|   0

| n =>
|   0
| (zero? n) =>
|   true
| (loop [acc 1 n num] (debux.common.util/insert-blank-line) (if (zero? n ... =>
|   6

7.3.2. defn/defn-/fn ~ recur without loop

If you use dbgn in defn/defn-/fn ~ recur form without loop, you will have the following exception. I am sorry about it, but this is inevitable due to the implementation restriction.
(dbgn (defn factorial [acc n]
        (if (zero? n)
          acc
          (recur (* acc n) (dec n)))))
REPL output
1. Caused by java.lang.UnsupportedOperationException
   Cannot recur across try


However, if you temporarily replace recur with function name itself, you can debug the form as follows. Be careful not to forget to recover function name itself to recur after debugging.
(dbgn (defn factorial [acc n]
        (if (zero? n)
          acc
          (factorial (* acc n) (dec n)))))

(factorial 1 3)
REPL output
{:ns examples.demo, :line 248}
dbgn: (defn factorial [acc n] (if (zero? n) acc (factorial (* acc n) (dec n) ... =>

| n =>
|   3
| (zero? n) =>
|   false
| acc =>
|   1
| (* acc n) =>
|   3
| (dec n) =>
|   2

|| n =>
||   2
|| (zero? n) =>
||   false
|| acc =>
||   3
|| (* acc n) =>
||   6
|| (dec n) =>
||   1

||| n =>
|||   1
||| (zero? n) =>
|||   false
||| acc =>
|||   6
||| (* acc n) =>
|||   6
||| (dec n) =>
|||   0

|||| n =>
||||   0
|||| (zero? n) =>
||||   true
|||| acc =>
||||   6
|||| (if (zero? n) acc (factorial (* acc n) (dec n))) =>
||||   6
||| (factorial (* acc n) (dec n)) =>
|||   6

7.4. How to register your own macros in using dbgn/clogn

  • If you have some error when analyzing some source code using dbgn/clogn, first of all, you have to figure out what type of macro (refer to Categorized 19 types of macros in dbgn/clogn) caused the error and then register the macro by using register-macros!.

  • You can see the registered macros by using show-macros.

API format
(register-macros! macro-type macros)

(show-macros)
(show-macros macro-type)

7.4.1. Clojure example

example/core.clj
(defmacro my-let [bindings & body]
  `(let ~bindings ~@body))

;; Registering your own macro
(register-macros! :let-type [my-let])

(dbg (show-macros :let-type))
(dbg (show-macros))

(dbgn (my-let [a 10 b (+ a 10)] (+ a b)))
REPL output
{:ns examples.demo, :line 261}
dbg: (show-macros :let-type) =>
|   {:let-type
|    #{clojure.core/when-let example.dbgn/my-let clojure.core/let
|      clojure.core/with-local-vars clojure.core/when-some clojure.core/dotimes
|      clojure.core/with-open clojure.core/with-redefs clojure.core/binding
|      clojure.core/with-in-str clojure.core/with-out-str clojure.core/when-first}}

{:ns examples.demo, :line 262}
dbg: (show-macros) =>
|   {:fn-type #{clojure.core/fn fn*},
|    :skip-arg-1-2-3-type #{},
|    :skip-form-itself-type
|    #{clojure.core/definterface clojure.core/defrecord clojure.core/deftype
|      finally clojure.core/gen-class clojure.core/definline catch
|      clojure.core/gen-interface clojure.core/defprotocol},
|    :case-type #{clojure.core/case},
|    :skip-arg-2-3-type #{clojure.core/areduce clojure.core/amap},
|    :skip-arg-1-type #{clojure.core/with-precision set!},
|    :let-type
|    #{clojure.core/when-let example.dbgn/my-let clojure.core/let
|      clojure.core/with-local-vars clojure.core/when-some
|      clojure.core/dotimes clojure.core/with-open clojure.core/with-redefs
|      clojure.core/binding clojure.core/with-in-str
|      clojure.core/with-out-str clojure.core/when-first},
|    :skip-arg-2-type #{clojure.core/as->},
|    :defn-type #{clojure.core/defn clojure.core/defn-},
|    :loop-type #{clojure.core.async/go-loop clojure.core/loop},
|    :for-type #{clojure.core/for clojure.core/doseq},
|    :def-type #{clojure.core/defonce def},
|    :if-let-type #{clojure.core/if-let clojure.core/if-some},
|    :letfn-type #{clojure.core/letfn},
|    :dot-type #{.},
|    :skip-arg-1-2-type #{},
|    :skip-all-args-type
|    #{clojure.core/proxy-super clojure.core/defmacro clojure.core/sync
|      clojure.core/declare clojure.core/refer-clojure clojure.core/memfn
|      clojure.core/extend-type new clojure.core/defstruct
|      clojure.core/defmulti clojure.core/ns clojure.core/proxy
|      clojure.core/extend clojure.core/extend-protocol var quote
|      clojure.core/reify clojure.core/import},
|    :expand-type
|    #{clojure.core/doto clojure.core/->> clojure.core/some->>
|      clojure.core/.. clojure.core/-> clojure.core/some->
|      clojure.core/cond-> clojure.core/condp clojure.core/import
|      clojure.core/cond->>},
|    :skip-arg-1-3-type #{clojure.core/defmethod}}

{:ns examples.demo, :line 264}
dbgn: (my-let [a 10 b (+ a 10)] (+ a b)) =>
| a =>
|   10
| (+ a 10) =>
|   20

| b =>
|   20
| (+ a b) =>
|   30
| (my-let [a 10 b (+ a 10)] (debux.common.util/insert-blank-line) (+ a b ... =>
|   30

7.4.2. ClojureScript example

example/macro.clj
(ns example.macro)

(defmacro my-let [bindings & body]
  `(let ~bindings ~@body))
example/core.cljs
(ns examples.demo
  (:require [debux.cs.core :as d :refer-macros [clog clogn dbg dbgn break]])
  (:require-macros [examples.macro :refer [my-let]]))

;; Registering your own macro
(d/register-macros! :let-type [my-let])

(dbg (d/show-macros :let-type))
(dbg (d/show-macros))

(clogn (my-let [a 10 b (+ a 10)] (+ a b)))
Output
{:ns examples.demo, :line 261}
dbg: (d/show-macros :let-type) =>
|   {:let-type
|    #{example.macro/my-let cljs.core/with-redefs cljs.core/binding
|      cljs.core/when-first cljs.core/let cljs.core/with-out-str
|      cljs.core/when-let cljs.core/when-some cljs.core/dotimes}}

{:ns examples.demo, :line 262}
dbg: (d/show-macros) =>
|   {:fn-type #{fn* cljs.core/fn},
|    :skip-arg-1-2-3-type #{},
|    :skip-form-itself-type
|    #{finally cljs.core/defprotocol cljs.core/defrecord cljs.core/deftype
|      cljs.core/js-comment cljs.core/js-inline-comment catch},
|    :case-type #{cljs.core/case},
|    :skip-arg-2-3-type #{cljs.core/amap cljs.core/areduce},
|    :skip-arg-1-type #{set! cljs.core/this-as},
|    :let-type
|    #{example.macro/my-let cljs.core/with-redefs cljs.core/binding
|      cljs.core/when-first cljs.core/let cljs.core/with-out-str
|      cljs.core/when-let cljs.core/when-some cljs.core/dotimes},
|    :skip-arg-2-type #{cljs.core/as->},
|    :defn-type #{cljs.core/defn- cljs.core/defn},
|    :loop-type #{cljs.core/loop},
|    :for-type #{cljs.core/doseq cljs.core/for},
|    :def-type #{cljs.core/defonce def},
|    :if-let-type #{cljs.core/if-some cljs.core/if-let},
|    :letfn-type #{cljs.core/letfn},
|    :dot-type #{.},
|    :skip-arg-1-2-type #{},
|    :skip-all-args-type
|    #{cljs.core/simple-benchmark cljs.core/defmulti cljs.core/specify!
|      cljs.core/goog-define cljs.core/import-macros cljs.core/specify
|      cljs.core/use cljs.core/use-macros cljs.core/extend-protocol new
|      cljs.core/import cljs.core/declare cljs.core/reify cljs.core/require
|      cljs.core/comment cljs.core/memfn cljs.core/require-macros var
|      quote cljs.core/refer-clojure cljs.core/extend-type cljs.core/defmacro},
|    :expand-type
|    #{cljs.core/.. cljs.core/some-> cljs.core/-> cljs.core/cond->>
|      cljs.core/import cljs.core/doto cljs.core/condp cljs.core/cond->
|      cljs.core/some->> cljs.core/->>},
|    :skip-arg-1-3-type #{cljs.core/defmethod}}
register macros
Figure 1. register-macros! example

8. Multiple use of dbg and dbgn

This feature applies to the multiple use of clog and clogn as well.

8.1. dbg inside dbgn or vice versa

dbg can be used inside dbgn or vice versa. For example, if you want to see the printed results of ->, ->>, let or comp in dbg when using dbgn, do it like this.

(defn my-fun [a b c]
  (dbgn (+ a b c
           (dbg (->> (range (- b a))
                     (map #(* % %))
                     (filter even?)
                     (take a)
                     (reduce +))))))

(my-fun 10 20 100)
REPL output:
{:ns examples.demo, :line 271}
dbgn: (+ a b c (dbg (->> (range (- b a)) (map (fn* [p1__3949#] (* p1__3949#  ... =>
| a =>
|   10
| b =>
|   20
| c =>
|   100

|{:ns examples.demo, :line 272}
|dbg: (->> (range (- b a)) (map (fn* [p1__41#] (* p1__41# p1__41#))) (filter ... =>
|| (range (- b a)) =>
||   (0 1 2 3 4 5 6 7 8 9)
|| (map (fn* [p1__41#] (* p1__41# p1__41#))) =>
||   (0 1 4 9 16 25 36 49 64 81)
|| (filter even?) =>
||   (0 4 16 36 64)
|| (take a) =>
||   (0 4 16 36 64)
|| (reduce +) =>
||   120
| (+ a b c (dbg (->> (range (- b a)) (map (fn* [p1__41#] (* p1__41# p1__ ... =>
|   250

8.2. Multiple dbgn and dbg

Multiple dbgn and dbg can be used together.

Example 1
(def n 10)

(defn add [a b]
  (dbgn (+ a b)))

(defn mul [a b]
  (dbgn (* a b)))

(dbgn (+ n (mul 3 4) (add 10 20)))
REPL output:
{:ns examples.demo, :line 290}
dbgn: (+ n (mul 3 4) (add 10 20)) =>
| n =>
|   10

|{:ns examples.demo, :line 288}
|dbgn: (* a b) =>
|| a =>
||   3
|| b =>
||   4
|| (* a b) =>
||   12
| (mul 3 4) =>
|   12

|{:ns examples.demo, :line 285}
|dbgn: (+ a b) =>
|| a =>
||   10
|| b =>
||   20
|| (+ a b) =>
||   30
| (add 10 20) =>
|   30
| (+ n (mul 3 4) (add 10 20)) =>
|   52


Example 2
(def n 10)

(defn add2 [a b]
  (dbg (+ a b)))

(defn mul2 [a b]
  (dbg (* a b)))

(dbgn (+ n (mul2 3 4) (add2 10 20)))
REPL output:
{:ns examples.demo, :line 299}
dbgn: (+ n (mul2 3 4) (add2 10 20)) =>
| n =>
|   10

|{:ns examples.demo, :line 297}
|dbg: (* a b) =>
||   12
| (mul2 3 4) =>
|   12

|{:ns examples.demo, :line 294}
|dbg: (+ a b) =>
||   30
| (add2 10 20) =>
|   30
| (+ n (mul2 3 4) (add2 10 20)) =>
|   52

9. Various options

  • The various options can be added and combined in any order after the form.

Table 4. debux macro options
Optionsdbgdbgnclogclognbreak

string

O

O

O

O

O

number

O

O

O

O

X

:if

O

O

O

O

O

:print

O

X

O

X

X

:dup

X

0

X

0

X

:style

X

X

O

O

X

:once

X

X

O

X

X

:js

X

X

O

O

X

  • Legend: O (supported), X (not supported)

9.1. String option

You can add your own message in a string and it will be printed between less-than and more-than signs like this.

(dbg (repeat 5 "x") "5 times repeat"))
; => ("x" "x" "x" "x" "x")
REPL output
{:ns examples.demo, :line 305}
dbg: (repeat 5 "x")   <5 times repeat> =>
|   ("x" "x" "x" "x" "x")

9.2. Number option

If you don’t specify a number after the form returning the coll data type, debux macros will print the default 100 items.

(dbgn (count (range 200)))
; => 200
REPL output
{:ns examples.demo, :line 309}
dbgn: (count (range 200)) =>
| (range 200) =>
|   (0 1 2 ...... 99 ...)
| (count (range 200)) =>
|   200

So, if you want to print less or more than default 100 items, specify the number explicitly like this.

(dbgn (count (range 200)) 200)
; => 200
REPL output
{:ns examples.demo, :line 311}
dbgn: (count (range 200)) =>
| (range 200) =>
|   (0 1 2 ...... 199)
| (count (range 200)) =>
|   200

The same rule applies to the case of evaluating an infinite lazy-seq. If you omit the number in evaluating an infinite lazy-seq, in the same manner it will print default 100 elements to prevent OutOfMemoryError.

(dbgn (take 5 (range)))
; => (0 1 2 3 4)
REPL output
{:ns examples.demo, :line 313}
dbgn: (take 5 (range)) =>
| (range) =>
|   (0 1 2 ...... 99 ...)
| (take 5 (range)) =>
|   (0 1 2 3 4)

The elements of the nested coll types will be printed as much as specified numbers.

(def m
  {:list (range)
   :vector (vec (range 100))
   :map (zipmap (range 100) (cycle [:a :b :c]))
   :set (set (range 100))})

(dbgn (count m) 5)
; => 4
REPL output
{:ns examples.demo, :line 328}
dbgn: (count m) =>
| m =>
|   {:list (0 1 2 3 4 ...),
|    :vector [0 1 2 3 4 ...],
|    :map {0 :a, 65 :c, 70 :b, 62 :c, 74 :c, ...},
|    :set #{0 65 70 62 74 ...}}
| (count m) =>
|   4
The Clojure source codes are the Clojure data structures as well, which is known as homoiconcity. When the debux macros traverse the Clojure source code trees, they enter the lists or vectors within the trees but don’t enter the maps or sets within the trees. So the codes themselves within the above map m aren’t printed in the middle of the evaluated results, because they are wrapped inside the map m.

If you want to change the default number globally, use set-print-length! function like this.

;; in Clojure
(set-print-length! 10)

(dbgn (take 5 (range)))
; => (0 1 2 3 4)
REPL output
{:ns examples.demo, :line 318}
dbgn: (take 5 (range)) =>
| (range) =>
|   (0 1 2 3 4 5 6 7 8 9 ...)
| (take 5 (range)) =>
|   (0 1 2 3 4)
;; in ClojureScript
(ns example.core
  (:require [debux.cs.core :as d :refer-macros [clog clogn dbg dbgn break]]))

(d/set-print-length! 10)

(clogn (take 5 (range)))

9.3. :if option

You can set :if option like this.

(doseq [i (range 10)]
  (dbg i :if (even? i)))
; => (0 1 2 3 4 5 6 7 8 9)
REPL output
{:ns examples.demo, :line 333}
dbg: i =>
|   0

{:ns examples.demo, :line 333}
dbg: i =>
|   2

{:ns examples.demo, :line 333}
dbg: i =>
|   4

{:ns examples.demo, :line 333}
dbg: i =>
|   6

{:ns examples.demo, :line 333}
dbg: i =>
|   8
The :print (or :p in brief) option applies only to dbg/clog.

If you don’t want to see the evaluated result itself but the result applied to another operations, use ‘:print one-arg-fn’ (or ‘:p one-arg-fn’) option like this.

(+ 10 (dbg (* 20 30) :print #(type %)))
; => 610

;; equivalent to the above
(+ 10 (dbg (* 20 30) :print type))
REPL output
{:ns examples.demo, :line 337}
dbg: (* 20 30) =>
|   java.lang.Long

The above example prints java.lang.Long, not 600

(def person
  {:name "Mark Volkmann"
   :address {:street "644 Glen Summit"
             :city "St. Charles"
             :state "Missouri"
             :zip 63304}
   :employer {:name "Object Computing, Inc."
              :address {:street "12140 Woodcrest Dr."
                        :city "Creve Coeur"
                        :state "Missouri"
                        :zip 63141}}})

(dbg person :p #(get-in % [:employer :address :city]))
REPL output
{:ns examples.demo, :line 339}
dbg: person =>
|   "Creve Coeur"

The above example prints the most inner :city part, not person itself.

9.5. :dup option

The same duplicate evaluated results are not printed by default as follows.

(dbgn (def my-function "my-function doc string"
        (fn [x] (* x x x))))

(my-function 10)
; => 1000
REPL output
{:ns examples.demo, :line 343}
dbgn: (def my-function "my-function doc string" (fn [x] (* x x x))) =>
| (fn [x] (* x x x)) =>
|   #function[example.core/eval24554/result--24229--auto----24555]
| (def my-function "my-function doc string" (fn [x] (* x x x))) =>
|   #'example.core/my-function

| x =>
|   10
| (* x x x) =>
|   1000

However, you can print the same duplicate evaluated values by :dup option.

(dbgn (def my-function "my-function doc string"
        (fn [x] (* x x x))) :dup)

(my-function 10)
; => 1000
REPL output
{:ns examples.demo, :line 349}
dbgn: (def my-function "my-function doc string" (fn [x] (* x x x))) =>
| (fn [x] (* x x x)) =>
|   #function[example.core/eval24554/result--24229--auto----24555]
| (def my-function "my-function doc string" (fn [x] (* x x x))) =>
|   #'example.core/my-function

| x =>
|   10
| x =>
|   10
| x =>
|   10
| (* x x x) =>
|   1000

You will sometimes need to print every duplicate evaluated value to see exactly what’s going on.

Compare the results of the next two examples.

(dbgn (loop [acc 1 n 3]
        (if (zero? n)
          acc
          (recur (* acc n) (dec n)))))

(dbgn (loop [acc 1 n 3]
        (if (zero? n)
          acc
          (recur (* acc n) (dec n)))) :dup)
REPL output
{:ns examples.demo, :line 355}
dbgn: (loop [acc 1 n 3] (if (zero? n) acc (recur (* acc n) (dec n)))) =>

| n =>
|   3
| (zero? n) =>
|   false
| acc =>
|   1
| (* acc n) =>
|   3
| (dec n) =>
|   2

| n =>
|   2
| acc =>
|   3
| (* acc n) =>
|   6
| (dec n) =>
|   1

| n =>
|   1
| acc =>
|   6
| (dec n) =>
|   0

| n =>
|   0
| (zero? n) =>
|   true
| (loop [acc 1 n 3] (debux.common.util/insert-blank-line) (if (zero? n)  ... =>
|   6

{:ns examples.demo, :line 360}
dbgn: (loop [acc 1 n 3] (if (zero? n) acc (recur (* acc n) (dec n)))) =>

| n =>
|   3
| (zero? n) =>
|   false
| acc =>
|   1
| n =>
|   3
| (* acc n) =>
|   3
| n =>
|   3
| (dec n) =>
|   2

| n =>
|   2
| (zero? n) =>
|   false
| acc =>
|   3
| n =>
|   2
| (* acc n) =>
|   6
| n =>
|   2
| (dec n) =>
|   1

| n =>
|   1
| (zero? n) =>
|   false
| acc =>
|   6
| n =>
|   1
| (* acc n) =>
|   6
| n =>
|   1
| (dec n) =>
|   0

| n =>
|   0
| (zero? n) =>
|   true
| acc =>
|   6
| (loop [acc 1 n 3] (debux.common.util/insert-blank-line) (if (zero? n)  ... =>
|   6

9.6. :style option (CSS Styling)

The following is the example of using clog and clogn in Chrome browser.

example/core.cljs
(ns example.core
  (:require [debux.cs.core :as d :refer-macros [clog clogn dbg dbgn break]]))

(clog (repeat 5 "x") "5 times repeat")
(clogn (repeat 5 (repeat 5 "x")) "25 times repeat")
clog
Figure 2. clog and clogn example

9.6.1. Predefined style keywords

You can style the form, using the following predefined keywords.

keywordabbreviation

:style

:s

:error

:e

:warn

:w

:info

:i

:debug

:d

(clog (+ 10 20) :style :error "error style")
(clog (+ 10 20) :style :warn "warn style")
(clog (+ 10 20) :style :info "info style")
(clog (+ 10 20) :style :debug "debug style")
(clog (+ 10 20) "debug style is default")

Or in brief

(clog (+ 10 20) :s :e "error style")
(clog (+ 10 20) :s :w "warn style")
(clog (+ 10 20) :s :i "info style")
(clog (+ 10 20) :s :d "debug style")
(clog (+ 10 20) "debug style is default")
clog style
Figure 3. Predefined style example

9.6.2. User-defined style

You can redefine the predefined styles or define your own new style by using merge-styles like this.

(d/merge-styles {:warn "background: #9400D3; color: white"
                 :love "background: #FF1493; color: white"})

(clog (+ 10 20) :style :warn "warn style changed")
(clog (+ 10 20) :style :love "love style")

;; You can style the form directly in string format in any way you want.
(clog (+ 10 20) :style "color:orange; background:blue; font-size: 14pt")
clog style user
Figure 4. User-defined style example

9.7. :once option

If you add :once (or :o in brief) option after the form, the same evaluated value will not be printed. This is a very useful feature, when you are debugging a game programming, where successive multiple frames usually have the same evaluated value.

(def a (atom 10))

; This will be printed.
(clog @a :once)

; This will not be printed,
; because the evaluated value is the same as before.
(clog @a :once)


(reset! a 20)

; This will be printed,
; because the evaluated value is not the same as before.
(clog @a :once)

; This will not be printed,
; because the evaluated value is the same as before.
(clog @a :once)
clog once
Figure 5. :once option example
(:once mode) string is appended after the form header to remind you of :once mode.

9.8. :js option

If :js option is added after the form, the JavaScript object will be printed as well, so you can inspect the internal structures of ClojureScript data types or the JavaScript objects returned by JavaScript interops in ClojureScript.

(clog {:a 10 :b 20} :js)
clog js
Figure 6. :js option example

10. Support for cljs-devtools

  • The clog/clogn of debux supports cljs-devtools since the version 0.5.9.

    cljs devtools
    Figure 7. cljs-devtools printing example
  • The development version debux library already includes the version 0.9.11 of cljs-devtools on its dependencies. Therefore, you don’t have to include cljs-devtools on the :dependencies section of your project file.

    Of course, you can include a newer version of devtools on the :dependencies section of your project file.
    Never use the development version debux in production. Instead, use the production version debux-stubs in production. See the details here.

    So if you want to use cljs-devtools printing, install it via :preloads cljs compiler option like this, following cljs-devtools installition guide.

    project.clj
    (defproject your-project  "0.1.0"
      ,,,,,,
      :cljsbuild {:builds [{:compiler {:preloads [devtools.preload]
                                       ,,,,,,}}]})

    Or install it manually like this.

    (ns your-project.devtools
      (:require [devtools.core :as devtools]))
    
    (devtools/install!)
  • Don’t forget to to turn on the Chrome DevTools' [Settings — Preferences — Console — Enable custom formatters], before using cljs-devtools printing.

    chrome devtools settings
    Figure 8. Enable custom formatters on Chrome DevTools
  • You should read Why some custom formatters were not rendered? before using cljs-devtools printing.

  • You can confiugre cljs-devtools in various ways as you like. See the details in cljs-devtools configuration.

11. dbg-last: Debugging inside the thread-last macro ->>

The dbg-last/clog-last macros must be used inside the thread-last macro ->>
The options of dbg-last/clog-last macros are the same as those of dbg/clog.

If you want to use dbg macro with its options inside the thread-last macro ->> like this, you will have an exception.

(->> (range 10)
     (filter odd?)
     (dbg 5 "after filter")
     (map inc))
; >> 1. Unhandled java.lang.IllegalArgumentException
;       Don't know how to create ISeq from: java.lang.Long

The dbg-last macro is to the rescue of this case.

(->> (range 20)
     (filter odd?)
     (dbg-last 5 "after filter")
     (map inc))
; => (2 4 6 8 10 12 14 16 18 20)
REPL output
{:ns examples.demo}
dbg: (filter odd? (range 20))   <after filter> =>
|   (1 3 5 7 9)

There is no problem in case of the dbg macro with its options inside the thread-first macro ->.

(-> (range 10)
    (conj 100)
    (dbg 5 "after conj")
    vec)
; => [100 0 1 2 3 4 5 6 7 8 9]
{:ns examples.demo, :line 374}
dbg: (conj (range 10) 100)   <after conj> =>
|   (100 0 1 2 3)

12. dbg-prn: Debugging the macros for ClojureScript at the macro-expansion time.

The function dbg-prn doesn’t follow the usage employed in dbg/clog. It is just another name of println which can be used at the macro-expansion time.
dbg-prn can be used inside the macros for Clojure.

See the detailed explaination here.

13. break examples

13.1. break options

You can use break to set the breakpoint in the source code like this. You can add string option for message, or :if option for conditional break.

(break)
(break "hello world")
(break :if (> 10 20) "this will not be printed")
(break :if (< 10 20) "10 is less than 20")

You can see the message in DevTools' console window.

break 1

13.2. Callstack, locals, etc

After setting the breakpoint, you can inspect the callstack, locals, etc. in the browser’s DevTools window.

(defn my-fun2
  [a {:keys [b c d] :or {d 10 b 20 c 30}} [e f g & h]]
  (break "in my-fun2")
  (clog [a b c d e f g h]))

(my-fun2 (take 5 (range)) {:c 50 :d 100} ["a" "b" "c" "d" "e"])

You can see the message in DevTools' console window.

break 2

13.3. :if option example

When using break, you can use :if like this.

(defn my-fun3 []
  (let [a 10
        b 20]
    (dotimes [i 1000]
      (break :if (= i 999) "in my-fun3"))))

(my-fun3)

break 4

14. How to configure debux

  • When (set-debug-mode! false) is run, the effects of set-ns-whitelist! and set-ns-blacklist! will be ignored.

    (set-debug-mode! false)
    
    ;; The folowings take no effect at all.
    (set-ns-whitelist! ["my-app.*" ])
    (set-ns-blacklist! ["my-app.foo" "my-app.bar.*"])
  • When set-ns-whitelist! and set-ns-blaklist! are both run like this, all my-app.* except my-app.foo will be run.

    (set-ns-whitelist! ["my-app.*" ])
    (set-ns-blacklist! ["my-app.foo" "my-app.bar.*"])

The following (in examples folder) is an example.

14.1. in Clojure

examples/project.clj
(defproject examples
  ,,,,,,
  :main examples.core
  ,,,,,,)
examples/src/clj/examples/core.clj
(ns examples.core
  (:require [debux.core :as d])
  (:gen-class))

(defn -main []
  (println "\nRunning debux examples...\n")

  ;(d/set-debug-mode! false)
  (d/set-ns-whitelist! ["examples.dbg*"])
  (d/set-ns-blacklist! ["examples.dbgn"])

  ;; You should require dynamically the namespaces that you want to load.
  (require 'examples.dbg)
  (require 'examples.options)
  (require 'examples.dbgn))

14.2. in ClojureScript

examples/project.clj
(defproject examples
  ,,,,,,
  :cljsbuild {:builds [{,,,,,,
                        :compiler {,,,,,,
                                   :preloads [examples.preload]
                                   ,,,,,,}}]})
examples/src/cljs/examples/preload.cljs
(ns examples.preload
  (:require [debux.cs.core :as d]))

;(d/set-debug-mode! false)
(d/set-ns-whitelist! ["examples.clog*"])
(d/set-ns-blacklist! ["examples.clogn"])

15. Using debux macros on Node.js

You had better use dbg/dbgn instead of clog/clogn on Node.js JavaScript console, because Node.js doesn’t support colors in its console.log function. The following shows the example.

example.node
(ns examples.node
  (:require [cljs.nodejs :as nodejs]
            [debux.cs.core :refer-macros [clog clogn dbg dbgn]] ))

(defn -main [& args]
  (dbgn (+ 2 (* 3 4)))
  (clogn (+ 2 (* 3 4))))

(set! *main-cli-fn* -main)
JavaScript console output on Node.js
{:ns examples.node :line 6}
dbgn: (+ 2 (* 3 4)) =>
| (* 3 4) =>
|   12
| (+ 2 (* 3 4)) =>
|   14

{:ns examples.node :line 7}
%cclogn: %c (+ 2 (* 3 4)) %c => color: #8b008b background: #ffc125; color: black color: black =>
| %c (* 3 4) %c => background: #ffc125; color: black color: black =>
|   12
| %c (+ 2 (* 3 4)) %c => background: #ffc125; color: black color: black =>
|   14

Of course, you should use the clog/clogn instead of dbg/dbgn in Electron apps on Node.js, because Electron supports colors in its console.log function.

16. Usage in ClojureScript on Browser REPL

You can use both dbg/dbgn and clog/clogn on the browser REPL. The following is an example about running the figwheel.

project.clj
(defproject examples "0.1.0-SNAPSHOT"
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [org.clojure/clojurescript "1.10.238"]
                 [philoskim/debux "0.6.4"]]
  :plugins [[lein-cljsbuild "1.1.6"]
            [lein-figwheel  "0.5.10"]]
  :source-paths ["src/clj"]
  :clean-targets ^{:protect false}
                 ["resources/public/js/app.js"
                  "resources/public/js/app.js.map"]
  :cljsbuild {:builds [{:id "dev"
                        :source-paths ["src/cljs"]
                        :figwheel true
                        :compiler {:main examples.core
                                   :asset-path "js/out"
                                   :output-to "resources/public/js/app.js"
                                   :output-dir "resources/public/js/out"
                                   :source-map true
                                   :optimizations :none} }]})

And then run figwheel like this on terminal window.

$ lein figwheel
Figwheel: Cutting some fruit, just a sec ...
Figwheel: Validating the configuration found in project.clj
Figwheel: Configuration Valid :)
Figwheel: Starting server at http://0.0.0.0:3449
Figwheel: Watching build - dev
Compiling "resources/public/js/app.js" from ["src/cljs"]...
Successfully compiled "resources/public/js/app.js" in 2.14 seconds.
Launching ClojureScript REPL for build: dev
Figwheel Controls:
          (stop-autobuild)                ;; stops Figwheel autobuilder
          (start-autobuild [id ...])      ;; starts autobuilder focused on optional ids
          (switch-to-build id ...)        ;; switches autobuilder to different build
          (reset-autobuild)               ;; stops, cleans, and starts autobuilder
          (reload-config)                 ;; reloads build config and resets autobuild
          (build-once [id ...])           ;; builds source one time
          (clean-builds [id ..])          ;; deletes compiled cljs target files
          (print-config [id ...])         ;; prints out build configurations
          (fig-status)                    ;; displays current state of system
          (figwheel.client/set-autoload false)    ;; will turn autoloading off
          (figwheel.client/set-repl-pprint false) ;; will turn pretty printing off
  Switch REPL build focus:
          :cljs/quit                      ;; allows you to switch REPL to another build
    Docs: (doc function-name-here)
    Exit: Control+C or :cljs/quit
 Results: Stored in vars *1, *2, *3, *e holds last exception object
Prompt will show when Figwheel connects to your application

After that, connect to http://localhost:3449 on your browser.

To quit, type: :cljs/quit
cljs.user=> (require '[debux.cs.core :refer-macros [clog clogn dbg dbgn break]])
nil

cljs.user=> (dbg (+ 1 2))

{:ns cljs.user :line 4}
dbg: (+ 1 2) =>
|   3
3

cljs.user=>

Now you can do anything in this browser REPL as in the Clojure REPL. When you evaluate dbg/dbgn in your ClojureScript source code, the result will go to both the REPL window and the browser’s console window. When you evaluate clog/clogn in your ClojureScript source code, the result will go only to your browser’s console window.

17. debux.el for Emacs CIDER user

Inserting or deleting dbg/dbgn/clog/clogn manually is very painful. As Emacs user I wrote debux.el for Emacs CIDER for my convenience. I think it’s not perfect but better than nothing. If you find it useful, append the following debux.el (which is in project root folder) to the ~/.emacs.d/init.el.

17.1. debux.el

Refer to the source code of debux.el here.

17.2. How to use

  • If you are editing on *.clj or *.cljc files, (dbg ...) or (dbgn ...) will be inserted or deleted.

  • If you are editing on *.cljs files, (clog ...) or (clogn ...) will be inserted or deleted.

17.2.1. Inserting dbg/clog or dbgn/clogn

When you double-click the left mouse button on one of the open parentheses and the following string is not dbg or clog, it will be inserted.

  • The v of the following examples marks the cursor position.

;; before
;; v
   (let [a 1 b 2]
     (+ a b))

;; after
   (dbg (let [a 1 b 2]
          (+ a b)))

When you double-click on a symbol, dbg or clog will be inserted as well.

;; before
;     v
   (+ a b)

;; after
   (+ (dbg a) b)

When you double-click on one of the open parentheses while pressing <Ctrl> key and the following string is not dbgn or clogn, it will be inserted.

;; before
;; v
   (defn foo [a b c]
     (* a b c))

;; after
   (dbgn (defn foo [a b c]
           (* a b c)))

17.2.2. Deleting dbg/clog/dbgn/clogn

When you double-click on one of the open parentheses and the following string is dbg, clog, dbgn or clogn, it will be deleted.

;; before
;; v
   (dbg (let [a 1 b 2]
          (+ a b)))

;; after
   (let [a 1 b 2]
     (+ a b))


;; before
;; v
   (dbgn (defn foo [a b c]
           (* a b c)))

;; after
   (defn foo [a b c]
     (* a b c))

18. License

Copyright © 2015—​2020 Young Tae Kim

Distributed under the Eclipse Public License either version 1.0 or any later version.

Can you improve this documentation? These fine people already did:
philoskim, Philos Kim & philos
Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close