:source-language: clojure :source-highlighter: coderay :sectnums: :imagesdir: ./doc/img :toc:
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.
[[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
(link: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.
WARNING: 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.
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:
[source] .... [philoskim/debux "0.9.1"] ....
and this to your production dependencies.
[source] .... [philoskim/debux-stubs "0.9.1"] ....
NOTE: You can see All change logs since v0.3.0 https://github.com/philoskim/debux/tree/master/doc/change-logs.adoc[here].
v0.9.1
** :simple
option deprecated. Instead, use :final
(or :f
) option.
See the details <<final-option, here>>.
v0.9.0
All the new features of this version are thanks to the link:https://github.com/philoskim/debux/pull/31[pull request] of link:https://github.com/gnl[George Lipov]. Thanks a lot again!
The minimum version of Clojure is upgraded from 1.8.0 to 1.10.0
ClojureScript dependency in Clojure removed.
** bb
(Babashka for Clojure) support added. See the details <<babashka, here>>.
*** nbb
(Babashka for Node) is not supported yet.
tap>
support added. See the details <<tap-output, here>>.
** set-tap-output!
function added.
set-date-time-fn!
function added.
** :simple
option added.
v0.8.3 ** link:https://github.com/hyperfiddle/electric[Electric] support added. See the details <<electric, here>>.
v0.8.2 ** A bug fixed: pull request link:https://github.com/philoskim/debux/pull/26[#26]
v0.8.1
** dbgt
and clogt
macro bugs fixed.
v0.8.0
dbgt
and clogt
macros for debugging transducers added. See the details
<<dbgt, here>>.
set-debug-level!
removed. Instead, use with-level
macro. See the details
<<with-level, here>>.
In Clojure, the following line should be included in your file.
[source] .... (use 'debux.core) ....
In ClojureScript, the following (:require pass:q[...])
line has to be included in your
file.
[source] .examples/core.cljs .... (ns examples.core (:require [debux.cs.core :as d :refer-macros [clog clogn dbg dbgn break clog clogn dbg dbgn break_]])) ....
{empty} +
[cols="^1m,^1m,^1m,^1m,^1m,^1m", options="header"] .Debugging API use
|===
| | dbg | dbgn | clog | clogn | break
| Clojure REPL | O | O | X | X | X | ClojureScript REPL | O | O | X | X | X | ClojureScript Browser console | O | O | O | O | O
|===
O
(supported), X
(not supported)//-
. dbg
/dbgn
can be used in Clojure REPL.
. dbg
/dbgn
can be used in ClojureScript REPL like
link:https://github.com/tomjakubowski/weasel[weasel] or
https://github.com/bhauman/lein-figwheel[figwheel].
** Refer to <
. dbg
/dbgn
, clog
/clogn
and break
can be used in the browser console window
like Chrome DevTools.
+
TIP: 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 <<style-option, here>>.
dbg
examplesNOTE: You can see every example source code of this document in https://github.com/philoskim/debux/tree/master/examples[examples] folder.
NOTE: 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.
[source] .... (* 2 (dbg (+ 10 20))) ; => 60 ....
[#eval-multiple-forms] Sometimes you need to see multiple forms evaluated. To do so, a literal vector form can be used like this.
[source] .... (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")] ....
[#eval-multiple-forms-with-dbgn]
You can use dbgn
for better results as well. See the detalis for dbgn
<<dbgn-examples,
here>>.
[source] .... (defn my-fun2 [a {:keys [b c d] :or {d 10 b 20 c 30}} [e f g & h]] (dbgn [a b c d e f g h]))
(my-fun2 (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")] ....
Generally, dbg
prints the evaluated result of the outermost form except for the
following eight special cases (pass:q[->]
, pass:q[->>]
, pass:q[some->]
,
pass:q[some->>]
, pass:q[cond->]
, pass:q[cond->>]
, let
, comp
).
pass:q[->]
or pass:q[->>]
When debugging the thread-first macro pass:q[->]
or thread-last macro pass:q[->>]
,
dbg
prints every expression in the thread macros.
This is an example of thread-first macro pass:q[->]
.
[source] .... (dbg (-> "a b c d" .toUpperCase (.replace "A" "X") (.split " ") first)) ;=> "X" ....
Another example.
[source] .... (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 pass:q[->>]
.
[source] .... (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 ....
If you want to debug one of the expressions within the thread macro pass:q[->]
or
pass:q[->>]
, don't do it like this.
[source] .... (-> {: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.
[source] .... (-> {: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.
[source] .... (->> [-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) ....
NOTE: 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 pass:q[->
] or pass:q[->>
] in this situation. If anyone knows
about it, please let me know.
See more examples <<dbg-last, here>>.
[[some-threading-macros]]
pass:q[some->]
or pass:q[some->>]
The thread macro pass:q[some->]
and pass:q[some->>]
are supported in dbg
and clog
.
[source] .Example 1 .... (dbg (some-> {:a 10} :b inc)) ....
[source] .Example 2 .... (dbg (some->> {:x 5 :y 10} :y (- 30))) ....
[[cond-threading-macros]]
pass:q[cond->]
or pass:q[cond->>]
The thread macro pass:q[cond->]
and pass:q[cond->>]
are supported in dbg
and clog
.
[source] .Example 1 .... (def a 10)
(dbg (cond-> a (even? a) inc (= a 20) ( 42) (= 5 5) ( 3))) ....
[source] .REPL output .... {:ns examples.lab, :line 60} dbg: (cond-> a (even? a) inc (= a 20) ( 42) (= 5 5) ( 3)) => | a => | 10 | (even? a) => | true | inc => | 11 | (= a 20) => | false | (= 5 5) => | true | (* 3) => | 33 ....
[source] .Example 2 .... (def b 10)
(dbg (cond->> b (even? b) inc (= b 20) (- 42) (= 2 2) (- 30))) ....
[source] .REPL output .... {:ns examples.lab, :line 65} dbg: (cond->> b (even? b) inc (= b 20) (- 42) (= 5 5) (- 30)) => | b => | 10 | (even? b) => | true | inc => | 11 | (= b 20) => | false | (= 5 5) => | true | (- 30) => | 19 ....
let
or comp
formWhen debugging let
form,
[source] .... (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,
[source] .... (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 ....
[[final-option]]
For example, if you want to see only the final result of the pass:q[->]
,
put the option :final
or :f
like this.
[source] .... (dbg (-> "a b c d" .toUpperCase (.replace "A" "X") (.split " ") first) :final) ;=> "X" ....
This option apliies to pass:q[->]
, pass:q[->>]
, pass:q[some->]
,
pass:q[some->>]
, pass:q[cond->]
, pass:q[cond->>]
, let
.
[[dbgn-examples]]
dbgn
examplesNOTE: 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.
[source] .... (dbgn (defn foo [a b & [c]] (if c ( a b c) ( a b 100))))
(foo 2 3) ; => 600
(foo 2 3 10) ; => 60 ....
{: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
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 <
[#macro-type-table, cols="^3m,<7m", options="header"]
.Categorized 19 types of macros in dbgn
/clogn
|===
| Macro types | Macros 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 | pass:q[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 | pass:q[.. -> ->> doto cond-> cond->> condp import some-> some->>]
| :dot-type | .
|===
:def-type
exampleThis type of macros have the first argument which must not be evaluated and can have
optional doc-string
argument.
[source] .... (dbgn (def my-function "my-function doc string" (fn [x] (* x x x))))
(my-function 10) ; => 1000 ....
{: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
:defn-type
exampleThis 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.
[source] .... (dbgn (defn add "add doc string" [a b] (+ a b)))
(add 10 20) ; => 30 ....
{:ns examples.demo, :line 92} dbgn: (defn add "add doc string" [a b] (+ a b)) =>
{empty} +
You can debug multiple-arity functions as well.
[source] .... (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 ....
{: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
{empty} +
You can have multiple dbgn
/clogn
s.
[source] .... (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 ....
{: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
:fn-type
exampleThis 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.
[[enhanced-readability]] [source] .... (dbgn (reduce (fn [acc i] (+ acc i)) 0 [1 5 9])) ; => 15 ....
{: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
{empty} +
[source] .Another example .... (dbgn (map #(* % 10) [1 5 9])) ; => (10 50 90) ....
{:ns examples.demo, :line 123} dbgn: (map (fn [p1__2514#] ( p12514# 10)) [1 5 9]) => | (fn* [p113193#] (try (clojure.core/reset! (:evals +debux-dbg-opts+) ... => | #object[example.dbgn$eval13194$result4709auto__13195 0x1b58788a "example.dbgn$eval13194$result4709auto__13195@1b58788a"] | [1 5 9] => | [1 5 9]
|| p113583# => || 1 || (* p113583# 10) => || 10
|| p113583# => || 5 || (* p113583# 10) => || 50
:let-type
exampleThis type of macros have the binding vector argument which must not be evaluated.
[source] .... (dbgn (let [a (+ 1 2) [b c] [(+ a 10) (* a 2)]] (- (+ a b) c))) ; => 10 ....
{: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]
:if-let-type
exampleThis type of macros are a little different from :let-type
macros in that they need only
one or two forms in their bodies.
[source] .... (def a* 10)
(dbgn (if-let [s a*] (+ s 100) false)) ; => 110 ....
:letfn-type
exampleThis type of macro has the special binding vector syntax which is a bit different from
:fn-type
.
[source] .... (dbgn (letfn [(twice [x] ( x 2)) (six-times [y] ( (twice y) 3))] (six-times 15))) ; => 90 ....
:loop-type
exampleThis 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 <
:for-type
exampleThis type of macros have a little different syntax from :let-type
macros, because it
can have :let
, :when
, or :while
clause.
[source] .... (dbgn (for [x [0 1 2 3 4 5] :let [y (* x 3)] :when (even? y)] y)) ; => (0 6 12) ....
{: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
:case-type
exampleThis type of macro has the special syntax. Refer to the details https://clojuredocs.org/clojure.core/case[here].
[source] .... (dbgn (let [mystr "hello"] (case mystr "" 0 "hello" (count mystr)))) ; => 5 ....
{empty} +
[source] .Another example .... (dbgn (case 'a (x y z) "x, y, or z" "default")) ; => "default" ....
:skip-arg-1-type
exampleThis type of macros have the first argument which must not be evaluated. So dbgn
/clogn
internally skips the evaluation of this argument.
[source] .... (dbgn (with-precision 10 (/ 1M 6))) ; => 0.1666666667M ....
:skip-arg-2-type
exampleThis type of macros have the second argument which must not be evaluated. So dbgn
/clogn
internally skips the evaluation of this argument.
[source] .... (dbgn (as-> 0 n (inc n) (inc n))) ; => 2 ....
:skip-arg-1-2-type
exampleThis 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.
:skip-arg-1-3-type
exampleThis type of macros have the first and third arguments which must not be evaluated. So
dbgn
/clogn
internally skips the evaluation of those arguments.
[source] .... (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) ; => "English greeting: Hello!"
(greeting french-map) ; => "French greeting: Bonjour!" ....
{: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!"
:skip-arg-2-3-type
exampleThis type of macros have the second and third arguments which must not be evaluated. So
dbgn
/clogn
internally skips the evaluation of those arguments.
[source] .... (let [xs (float-array [1 2 3])] (dbgn (areduce xs i ret (float 0) (+ ret (aget xs i))))) ; => 6.0 ....
:skip-arg-1-2-3-type
exampleThis 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.
:skip-all-args-type
exampleThis type of macros ignores all the arguments and prints the outermost form and its result.
[source] .... (dbgn (defmacro unless [pred a b] `(if (not ~pred) ~a ~b))) ....
:skip-form-itself-type
exampleThis type of macros ignores the form itself and prints nothing.
[source] .... (dbgn (try (/ 1 0) (catch ArithmeticException e (str "caught exception: " (.getMessage e))))) ....
NOTE: The evaluated resuts of the catch
form are not printed in the above example.
:expand-type
exampleThis type of macros will be expanded and then the output will be printed.
[source] .... (dbgn (-> "a b c d" .toUpperCase (.replace "A" "X") (.split " ") first)) ; => "X" ....
{empty} +
[source] .Another example .... (dbgn (.. "fooBAR" toLowerCase (contains "ooba"))) ; => true ....
{empty} +
[source] .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"] ....
{: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 | G14051 => | [] | (conj G14051 "x is odd") => | ["x is odd"] | (if (odd? x) (conj G14051 "x is odd") G14051) => | ["x is odd"] | y => | 2 | (rem y 3) => | 2 | (zero? (rem y 3)) => | false | G14051 => | ["x is odd"] | (if (zero? (rem y 3)) (conj G14051 "y is divisible by 3") G__14051) => | ["x is odd"]
:dot-type
example[source] .... (dbgn (. (java.util.Date.) getMonth)) ; => 5 ....
[#recur-support]
recur
[cols="^1m,^1m,^1m", options="header"]
.The forms including recur
|===
| | dbgn | clogn | loop ~ recur | O | O | defn/defn-/fn ~ recur | △ | △
|===
O
(supported), △
(limitedly supported)loop
~ recur
You can see the evaluated results of the form which incldues loop
~ recur
by using
dbgn
in Clojure and ClojureScript.
[source] .... (dbgn (loop [acc 1 n 3] (if (zero? n) acc (recur (* acc n) (dec n))))) ; => 6 ....
{: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
{empty} +
[source] .Another example .... (dbgn (defn fact [num] (loop [acc 1 n num] (if (zero? n) acc (recur (* acc n) (dec n))))))
(fact 3) ; => 6 ....
{: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
defn
/defn-
/fn
~ recur
without loop
IMPORTANT: 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.
[source] .... (dbgn (defn factorial [acc n] (if (zero? n) acc (recur (* acc n) (dec n))))) ....
{empty} +
TIP: 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.
[source] .... (dbgn (defn factorial [acc n] (if (zero? n) acc (factorial (* acc n) (dec n)))))
(factorial 1 3) ; => 6 ....
{: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
[#register-macros]
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 <register-macros!
.
You can see the registered macros by using show-macros
.
[source] .API format .... (register-macros! macro-type macros)
(show-macros) (show-macros macro-type) ....
[source] .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))) ....
{: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
[source] .example/macro.clj .... (ns example.macro)
(defmacro my-let [bindings & body] `(let ~bindings ~@body)) ....
[source] .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))) ....
{: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}}
image::register-macros.png[title="register-macros! example", width=750]
[#multiple-use]
dbg
and dbgn
NOTE: This feature applies to the multiple use of clog
and clogn
as well.
dbg
inside dbgn
or vice versadbg
can be used inside dbgn
or vice versa. For example, if you want to see the printed
results of pass:q[->
], pass:q[->>
], let
or comp
of dbg
in more compact way than
only using dbgn
, do it like this.
[source] .... (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) ; => 250 ....
{:ns examples.demo, :line 271} dbgn: (+ a b c (->> (range (- b a)) (map (fn [p1__3949#] ( p1__3949# ... => | a => | 10 | b => | 20 | c => | 100
In other words, dbg
can be used selectively inside dbgn
like this, if you want to
avoid printing a deeply nested structure inside dbgn
.
[source] .... (let [a 10 b 9 c 8 d 7 e 6 f 5 g 4 h 3] (dbgn ( a b (dbg (+ c d (- e f ( g h))))))) ; => 360 ....
The above dbg
will prevent dbgn
from printing (+ c d (- e f (* g h))))
recursively.
{:ns example.demo, :line 15} dbgn: ( a b (+ c d (- e f ( g h)))) => | a => | 10 | b => | 9
dbgn
and dbg
You can use multiple dbgn
or dbg
.
[source] .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))) ; => 52 ....
{: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
{empty} +
[source] .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))) ; => 52 ....
{: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
[[safe-debugging-in-multi-threads]]
The Debux macros dbg
, dbgn
, dbg-last
in Clojure support the safe debugging in
multi-threads since the version 0.7.1.
The following example shows that the messages produced by the dbg
are printed in their
own separate units, not mixed by one another.
[source] .... (defn my-fn [thread-no] (dbg (-> "a b c d" .toUpperCase (.replace "A" "X") (.split " ") first) :msg (str "thread-no: " thread-no)))
(future (Thread/sleep 1000) (my-fn 1))
(future (Thread/sleep 1000) (my-fn 2))
(future (Thread/sleep 1000) (my-fn 3))
(dbg (* 2 5))
(shutdown-agents) ....
{:ns examples.lab, :line 45} dbg: (* 2 5) => | 10
{:ns examples.lab, :line 26} dbg: (-> "a b c d" .toUpperCase (.replace "A" "X") (.split " ") first) <thread-no: 1> => | "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"
{:ns examples.lab, :line 26} dbg: (-> "a b c d" .toUpperCase (.replace "A" "X") (.split " ") first) <thread-no: 3> => | "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"
[[dbgt]]
dbgt
examplesNOTE: The features of clogt
are almost the same as those of dbgt
.
dbgt
and clogt
macros are for debugging transducers.
Special thanks to link:https://github.com/green-coder/transducer-exercises/blob/master/solution/debug.clj[Vincent Cantin] for the idea and inspiration for my writing these macros.
[source] .Example .... (transduce (dbgt (filter odd?)) conj (range 5)) ....
{:ns examples.lab, :line 5} dbgt: (filter odd?) |> 0 |< []
|> 1 |< [1]
|> 2 |< [1]
|> 3 |< [1 3]
[source] .Example .... (transduce (dbgt (comp (map inc) (filter odd?))) conj (range 5)) ....
{:ns examples.lab, :line 8} dbgt: (comp (map inc) (filter odd?)) |> 0 ||> 1 ||< [1] |< [1]
|> 1 ||> 2 ||< [1] |< [1]
|> 2 ||> 3 ||< [1 3] |< [1 3]
|> 3 ||> 4 ||< [1 3] |< [1 3]
[cols="^1m,^1m,^1m,^1m,^1m,^1m,^1m,^1m", options="header"] .debux macro options |===
| Optio ns | dbg | dbgn | clog | clogn | dbgt | clogt | break
| string | O | O | O | O | O | O | O | :msg or :m | O | O | O | O | O | O | X | number | O | O | O | O | O | O | X | :if | O | O | O | O | O | O | O | :locals or :l | O | O | O | O | O | O | X | :print or :p | O | X | O | X | X | X | X | :dup | X | 0 | X | 0 | X | X | X | :level | O | O | O | O | O | O | X | :final or :f | O | X | O | X | X | X | X | :style or :s | X | X | O | O | X | O | X | :once or :o | X | X | O | X | X | X | X | :js | X | X | O | O | X | X | X
|===
O
(supported), X
(not supported)You can add your own message in a string and it will be printed between less-than and more-than signs like this.
[source] .... (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") ....
[[msg-option]]
:msg (or :m)
optionSometimes you need to create the message dynamically. This option can be useful in the multi-threads programming like this.
[source] ....
(defn my-fn2 [thread-no] (dbg (* thread-no (+ 10 20)) :msg (str "thread-no: " thread-no)))
(future (Thread/sleep 3000) (my-fn2 1))
(future (Thread/sleep 1000) (my-fn2 2))
(future (Thread/sleep 2000) (my-fn2 3))
(dbg (* 10 5))
(shutdown-agents) ....
{:ns examples.lab, :line 20} dbg: (* 10 5) => | 50
{:ns examples.lab, :line 6} dbg: (* thread-no (+ 10 20)) <thread-no: 2> => | 60
{:ns examples.lab, :line 6} dbg: (* thread-no (+ 10 20)) <thread-no: 3> => | 90
If the above String
option and this :msg
option both exist, the :msg
option has the
higher precedence.
[[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.
[source] .... (dbgn (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 ....
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
.
[source] .... (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) ....
[[nested-coll]]
The elements of the nested coll
types will be printed as much as specified numbers.
[source] .... (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 ....
NOTE: 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
.
[[set-print-length]]
If you want to change the default number globally, use set-print-length!
function
like this.
[source] .... ;; in Clojure (set-print-length! 10)
(dbgn (take 5 (range))) ; => (0 1 2 3 4) ....
[source] .... ;; 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))) ....
:if
optionYou can set :if
option like this.
[source] .... (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 ....
[[local-option]]
:locals
(or :l
) optionThe :locals
option is added, according to the request
link:https://github.com/philoskim/debux/issues/19[#issue 19].
[source] .... (let [x 10 y 20] (dbg (+ x y) :locals) (dbg (-> 100 inc inc) :l)
(dbgn (-> 200 inc inc) :l)) ....
[source] .REPL output .... {:ns examples.lab, :line 11} dbg: (+ x y) => | :locals => | {x 10, y 20}
| 30
{:ns examples.lab, :line 12} dbg: (-> 100 inc inc) => | :locals => | {x 10, y 20}
| 100 => | 100 | inc => | 101 | inc => | 102
{:ns examples.lab, :line 14} dbgn: (-> 200 inc inc) => | :locals => | {x 10, y 20}
| (inc 200) => | 201 | (inc (inc 200)) => | 202 ....
[[print-option]]
:print
(or :p
) optionIMPORTANT: 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.
[source] .... (+ 10 (dbg (* 20 30) :print #(type %))) ; => 610
;; equivalent to the above (+ 10 (dbg (* 20 30) :print type)) ; => 610 ....
The above example prints java.lang.Long
, not 600
[source] .... (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])) ....
The above example prints the most inner :city
part, not person
itself.
[#dup-option]
:dup
optionThe same duplicate evaluated results are not printed by default as follows.
[source] .... (dbgn (def my-function "my-function doc string" (fn [x] (* x x x))))
(my-function 10) ; => 1000 ....
{: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
However, you can print the same duplicate evaluated values by :dup
option.
[source] .... (dbgn (def my-function "my-function doc string" (fn [x] (* x x x))) :dup)
(my-function 10) ; => 1000 ....
{: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
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.
[source] .... (dbgn (loop [acc 1 n 3] (if (zero? n) acc (recur (* acc n) (dec n))))) ; => 6
(dbgn (loop [acc 1 n 3] (if (zero? n) acc (recur (* acc n) (dec n)))) :dup) ; => 6 ....
{: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
[[with-level]]
:level
optionYou can set the debug level by using with-level
macro and specify :level
option as
follows. You can specify the debug levels with any positive numbers including the floating
point numbers. The with-level
macro uses a dynamic var of Clojure internally, so you can
nest with-level
macros to whatever level you want.
[source] .Example 1 .... ;; The default debug level is 0. (dbg (+ 10 20)) (dbg (+ 10 20 3) :level 3) (dbg (+ 10 20 5) :level 5) ....
{:ns examples.lab, :line 18} dbg: (+ 10 20) => | 30
{:ns examples.lab, :line 19} dbg: (+ 10 20 3) => | 33
[source] .Example 2 .... (with-level 3 (dbg (+ 10 20)) (dbg (+ 10 20 3) :level 3) (dbg (+ 10 20 5) :level 5)) ....
{:ns examples.lab, :line 12} dbg: (+ 10 20 3) => | 33
[source] .Example 3 .... (defn my-add [a b] (dbg (+ a b) :level 2))
(defn my-sub [a b] (dbg (- a b) :level 3))
(with-level 3 (dbg (my-add 10 20)) (dbg (my-sub 100 10))
(with-level 0 (dbg (* 10 2)))) ....
{:ns examples.lab, :line 20} dbg: (- a b) => | 90
:final
(or :f
) optionSee the details <<final-option, here>>.
[[style-option]]
:style
(or :s
) option (CSS Styling: CLJS only)The following is the example of using clog
and clogn
in Chrome browser.
[source] .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") ....
image::clog.png[title="clog and clogn example", width=650]
You can style the form, using the following predefined keywords.
[cols="^,^", options="header", width="30"] |===
| keyword | abbreviation | :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") ....
image::clog-style.png[title="Predefined style example", width=700]
You can redefine the predefined styles or define your own new style by using
merge-styles
like this.
[source] .... (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") ....
image::clog-style-user.png[title="User-defined style example", width=650]
:once
(or :o
) option (CLJS only)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.
[source] .... (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) ....
image::clog-once.png[title=":once option example", width=700]
NOTE: (:once mode)
string is appended after the form header to remind you of :once
mode.
:js
option (CLJS only)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) ....
image::clog-js.png[title=":js option example", width=800]
break
examples (CLJS only)break
optionsYou 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.
[source] .... (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.
image:break-1.png[title="break examples", width=650]
After setting the breakpoint, you can inspect the callstack, locals, etc. in the browser's DevTools window.
[source] .... (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.
image:break-2.png[width=750]
:if
option exampleWhen using break
, you can use :if
like this.
[source] .... (defn my-fun3 [] (let [a 10 b 20] (dotimes [i 1000] (break :if (= i 999) "in my-fun3"))))
(my-fun3) ....
image:break-4.png[]
[[tagged-literals]]
#d/dbg
, #d/dbgn
, #d/dbgt
,#d/clog
, #d/clogn
, #d/clogt
If you don't use the above options at all, you can use the tagged literals: #d/dbg
, #d/dbgn
, #d/dbgt
,#d/clog
, #d/clogn
, #d/clogt
. They behave exactly in the same way as their
counterparts dbg
, dbgn
, clog
, clogn
. So in the no options case, you don't have
to wrap the form by the parentheses any more, if you want.
[source] .Example 1 ....
....
[source] .REPL output .... {:ns examples.lab, :line 5} dbg: (+ 1 2 (* 3 4)) =>
|{:ns examples.lab, :line 5} |dbg: (* 3 4) => || 12 | 15 ....
[source] .Example 2 ....
....
[source] .REPL output .... {:ns examples.lab, :line 7} dbgn: (+ ( 2 5) (+ 10 ( 3 4))) => | (* 2 5) => | 10
|{:ns examples.lab, :line 7} |dbg: (+ 10 ( 3 4)) => || 22 | (+ ( 2 5) (+ 10 (* 3 4))) => | 32 ....
[source] .Example 3 ....
....
[source] .REPL output .... {:ns examples.lab, :line 9} dbg: (+ ( 2 5) (+ 10 ( 3 4))) =>
|{:ns examples.lab, :line 9} |dbgn: (+ 10 ( 3 4)) => || ( 3 4) => || 12 || (+ 10 (* 3 4)) => || 22 | 32 ....
You can comment out the tagged literals temporarily, by appending _
after #
like this.
[source] ....
....
[#dbg-last]
dbg-last
: Debugging inside the thread-last macro pass:q[->>
]IMPORTANT: The dbg-last
/clog-last
macros must be used inside the thread-last macro
pass:q[->>
]
NOTE: 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
pass:q[->>
] like this, you will have an exception.
[source] .... (->> (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.
[source] .... (->> (range 20) (filter odd?) (dbg-last 5 "after filter") (map inc)) ; => (2 4 6 8 10 12 14 16 18 20) ....
There is no problem in case of the dbg
macro with its options inside the thread-first
macro pass:q[->
].
[source] .... (-> (range 10) (conj 100) (dbg 5 "after conj") vec) ; => [100 0 1 2 3 4 5 6 7 8 9] ....
[[dbg-prn]]
dbg-prn
: Debugging the macros for ClojureScript at the macro-expansion time.CAUTION: 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.
NOTE: dbg-prn
can be used inside the macros for Clojure.
See the detailed explaination link:doc/macro-debugging-in-clojurescript.adoc[here].
[[tap-output]]
tap>
addedOnly dbg
, dbgn
and dbgt
will call the functions registered by add-tag
,
using tap>
function internally.
[cols="^2m,^1m,^1m,^1m,^1m,^1m,^1m, options="header"] |=== | | dbg | dbgn | clog | clogn | dbgt | clogt
| set-tap-output! | O | O | X | X | O | X |===
The following is an example of using tap>
support in Debux.
[source] .events/tap-output.clj .... (ns examples.tap-output (:require [debux.tap-output :as d] [java-time.api :as jt]) (:gen-class))
(defn log [x] (spit "event.log" (str x \newline) :append true))
(defn my-date-time [] (->> (jt/local-date-time) (jt/format :iso-date-time) ))
(defn -main [] (println "\nRunning debux examples...\n")
;; add log function to tap (add-tap log)
;; For example, if you want to log the results of dbg/dbgn/dbgt ;; Firstly, run set-tap-output! function like this. (d/set-tap-output! true)
;; Optionally run set-date-time-fn! like this. ;; This will add :time info to the src-info line additionally. (d/set-date-time-fn! my-date-time)
(d/dbg (+ 10 20)) (d/dbgn (+ 10 (* 2 3))) (transduce (dbgt (comp (map inc) (filter odd?))) conj (range 5))) ....
event.log
file will have the follwing logs.
{:ns examples.tap-output, :line 30, :time "2023-10-18T20:40:56.183613"} dbg: (+ 10 20) => | 30
{:ns examples.tap-output, :line 31, :time "2023-10-18T20:40:56.191472"} dbgn: (+ 10 ( 2 3)) => | ( 2 3) => | 6 | (+ 10 (* 2 3)) => | 16
{:ns examples.tap-output, :line 32, :time "2023-10-18T20:40:56.192775"} dbgt: (comp (map inc) (filter odd?)) |> 0 ||> 1 ||< [1] |< [1]
|> 1 ||> 2 ||< [1] |< [1]
|> 2 ||> 3 ||< [1 3] |< [1 3]
|> 3 ||> 4 ||< [1 3] |< [1 3]
[[cljs-devtools]]
cljs-devtools
addedclog
/clogn
of debux
supports
link:https://github.com/binaryage/cljs-devtools[cljs-devtools] since the version
0.5.9
.image::cljs-devtools.png[title="cljs-devtools printing example", width=700]
[[set-cljs-devtools]]
cljs-devtools
printing in debux,** firstly, call the set-cljs-devtools!
function in your source code explicitly like
this.
+
[source]
....
(debux.cs.core/set-cljs-devtools! true) ;; the default value is false
....
cljs-devtools
via :preloads
cljs compiler option like this,
following
link:https://github.com/binaryage/cljs-devtools/blob/master/docs/installation.md[cljs-devtools
installition guide].
+
[listing]
.project.clj(ns your-project.devtools (:require [devtools.core :as devtools]))
Don't forget to to turn on the Chrome DevTools' [Settings -- Preferences -- Console --
Enable custom formatters], before using cljs-devtools
printing.
image::chrome-devtools-settings.png[title="Enable custom formatters on Chrome DevTools", width=850]
You should read link:https://github.com/binaryage/cljs-devtools/blob/master/docs/faq.md#why-some-custom-formatters-were-not-rendered[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
link:https://github.com/binaryage/cljs-devtools/blob/master/docs/configuration.md[cljs-devtools
configuration].
[[babashka]]
bb
(Babashka for Clojure) added.The running exampls is under the examples
subproject folder of this project.
[source] .examples/bb.edn .... {:paths ["src/clj"] :deps {philoskim/debux {:mvn/version "0.9.1"} }} ....
examples
subproject root folder[[electric]]
Electric
addedlink:https://github.com/hyperfiddle/electric[Electric] has some problems on expanding the user-defined macros. See link:https://github.com/hyperfiddle/electric/issues/38[here] for the related issue.
So according to the advice of dustingetz, the author of Electric, I created the new namespaces and wrote some macros for Electric in Debux as follows.
;;; The server side macros for Electric debux.electric namespace: dbg, dbgn, dbgt, dbg-last, dbg, dbgn, dbgt, dbg-last
[source] .... (ns user.demo-toggle (:require [hyperfiddle.electric :as e] [hyperfiddle.electric-dom2 :as dom] [hyperfiddle.electric-ui4 :as ui]
clog_ clogn_ clogt_ clog-last_
dbg dbgn dbgt dbg-last
dbg_ dbgn_ dbgt_ dbg-last_]]) ))
(e/def x (e/server (e/watch !x))) ; reactive signal derived from atom
(e/defn Toggle [] (e/client (dom/h1 (dom/text "Toggle Client/Server"))
(dom/div
(dom/text "number type here is: "
(case x
true (e/client (clogn (pr-str (type 1)))) ;; <-- Here
false (e/server (dbgn (pr-str (type 1))) )))) ;; <-- Here
(dom/div (dom/text "current site: "
(case x
true "ClojureScript (client)"
false "Clojure (server)")))
(ui/button (e/fn []
(e/server
(swap! !x not)))
(dom/text "toggle client/server"))))
....
[[source-info-mode]]
If you want to turn off the source info line printing in the debux
macros, use
(set-source-info-mode! false)
. The :ns
and :line
source info line will not be
printed.
[source] .... (set-source-info-mode! false)
(dbg (+ 2 3)) (dbgn (* 10 (+ 2 3)))
(set-source-info-mode! true)
(dbg (+ 20 30)) (dbgn (* 10 (+ 2 3))) ....
dbg: (+ 2 3) => | 5
dbgn: ( 10 (+ 2 3)) => | (+ 2 3) => | 5 | ( 10 (+ 2 3)) => | 50
{:ns examples.lab, :line 11} dbg: (+ 20 30) => | 50
[[set-line-bullet]]
You can change the default line bullet "|
" by using set-line-bullet!
as follows.
[source] .... (set-line-bullet! ";") (dbg (+ 20 30)) (dbgn (* 10 (+ 2 3)))
(set-line-bullet! " ") (dbg (+ 20 30)) (dbgn (* 10 (+ 2 3))) ....
{:ns examples.lab, :line 11} dbg: (+ 20 30) => ; 50
{:ns examples.lab, :line 12} dbgn: ( 10 (+ 2 3)) => ; (+ 2 3) => ; 5 ; ( 10 (+ 2 3)) => ; 50
{:ns examples.lab, :line 17} dbg: (+ 20 30) => 50
[[temporal-turn-off]]
You can temporarily turn off the debux macros by appending pass:q[_]
after the existing
debux macro names or turn off the tagged literals by appending pass:q[_]
after #
.
[cols="m,m,m,m", options="header", width=75%] |===
^| Macros ^| turning-off ^| Tagged literals ^| turning-off
| dbg | dbg_ | #d/dbg | #d/dbg | dbgn | dbgn | #d/dbgn | #d/dbgn | dbgt | dbgt | #d/dbgt | #d/dbgt | dbg-prn | dbg-prn | | | dbg-last | dbg-last_ | |
| clog | clog_ | #d/clog | #d/clog | clogn | clogn | #d/clogn | #d/clogn | clogt | clogt | #d/clogt | #d/clogt | clog-last | clog-last | |
| break | break_ | |
|===
[[debux-config]]
(set-debug-mode! false)
is run, the effects of set-ns-whitelist!
and
set-ns-blacklist!
will be ignored.[source] .... (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."]) ....
set-ns-whitelist!
and set-ns-blaklist!
are both run like this, all my-app.*
except my-app.foo
will be run.[source] .... (set-ns-whitelist! ["my-app." ]) (set-ns-blacklist! ["my-app.foo" "my-app.bar."]) ....
The following (in https://github.com/philoskim/debux/tree/master/examples[examples] folder) is an example.
[source] .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)) ....
[source] .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"]) ....
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.
[source] .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) ....
{:ns examples.node :line 6} dbgn: (+ 2 ( 3 4)) => | ( 3 4) => | 12 | (+ 2 (* 3 4)) => | 14
Of course, you should use the clog
/clogn
instead of dbg
/dbgn
in
link:https://electronjs.org/[Electron] apps on Node.js, because Electron supports colors
in its console.log
function.
[#browser-repl]
You can use both dbg
/dbgn
and clog
/clogn
on the browser REPL. The following is
an example about running the link:https://github.com/bhauman/lein-figwheel[figwheel].
[source] .project.clj .... (defproject examples "0.1.0-SNAPSHOT" :dependencies [[org.clojure/clojure "1.10.0"] [org.clojure/clojurescript "1.10.238"] [philoskim/debux "0.9.1"]] :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.
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
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.
debux.el
for Emacs CIDER userInserting 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
.
debux.el
Refer to the source code of debux.el
https://github.com/philoskim/debux/blob/master/debux.el[here].
If you are editing on \*.clj
or *.cljc
files, pass:q[(dbg ...)
] or pass:q[(dbgn ...)
] will be inserted or deleted.
If you are editing on *.cljs
files, pass:q[(clog ...)
] or pass:q[(clogn ...)
] will
be inserted or deleted.
dbg
/clog
] or pass:q[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.
v
of the following examples marks the cursor position.[source] .... ;; 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.
[source] .... ;; 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.
[source] .... ;; before ;; v (defn foo [a b c] (* a b c))
;; after (dbgn (defn foo [a b c] (* a b c))) ....
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.
[source] .... ;; 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)) ....
Copyright © 2015--2024 Young Tae Kim
Distributed under the Eclipse Public License either version 1.0 or any later version.