Port of Python's IceCream to Clojure(Script).
(require '[icecream.icecream :as icecream :refer [ic]])
Works on functions, special forms and macros:
;; function
(ic (+ 1 1))
;; "ic| (+ 1 1): 2"
;; => 2
;; special form
(ic (if true :yes :no))
;; "ic| (if true :yes :no): :yes"
;; => :yes
;; macro
(ic (when true :yes))
;; "ic| (when true :yes): :yes"
;; => :yes
Smartly outputs scalars directly:
(ic 1)
;; "ic| 1"
;; => 1
(ic :a)
;; "ic| :a"
;; => :a
(ic 'a)
;; "ic| 'a"
;; => a
(ic "hello")
;; "ic| "hello""
;; => "hello"
(ic nil)
;; "ic| nil"
;; => nil
Behavior can be altered through the use of dynamic variables:
;; disable
(binding [icecream/*enabled* false]
(ic 1))
;; => 1
;; custom output function
(require '[taoensso.timbre :as timbre
:refer [debug]])
(binding [icecream/*output-function* #(debug %)
;; NB: you typically want this one as well, explained later
icecream/*include-file-location-in-context* false]
(ic 1))
;; 21-07-07 20:01:03 homebase DEBUG [icecream-test.core:131] - ic| 1
;; => 1
;; custom prefix - constant
(binding [icecream/*prefix* "hello: "]
(ic 1))
;; "hello: 1"
;; => 1
;; custom prefix - function
(require '[tick.alpha.api :as t])
(binding [icecream/*prefix* #(str (inst-ms (t/now)) "| ")]
(ic 1))
;; "1619103609280| 1"
;; => 1
;; systematically include call context
(binding [icecream/*include-context* true]
(test 1))
;; "ic| form-init4003934083635828251.clj:6 in icecream-test.core/eval51124- 1
;; => 1
;; same but don't include file name / line number
(binding [icecream/*include-context* true
icecream/*include-file-location-in-context* false]
(test 1))
;; "ic| icecream-test.core/eval51124- 1
;; => 1
;; smartly include call context (only when inside function calls)
(binding [icecream/*include-context* :smart]
(ic 1))
;; "ic| 1"
;; => 1
(defn test [v]
(ic v))
(binding [icecream/*include-context* :smart]
(test 1))
;; "ic| core.clj:6 in icecream-test.core/test- 1
;; => 1
(binding [icecream/*include-context* false]
(test 1))
;; "ic| 1
;; => 1
;; when calling `ic` w/ no args, `*include-context*` is systematically considered true
(ic)
;; "ic| form-init4003934083635828251.clj:6 in icecream-test.core/eval51125
;; => nil
Introspection (when *include-context*
is true
or :smart
) won't work in some cases. Typically in multi-methods or when calling dynamically created lambda functions.
You might want to take a look at the native tap API.
This blog post describe how it can be used for debugging purposes.
It allows binding several print destinations (akin to iceream's *output-function*
config). This way you could both log in the REPL and forward say portal.
One notable difference is that tap>
doesn't return the evaluated form value, but instead a boolean indicating if the action to output succeeded. As such it cannot be inserted as easily as other solutions (inside function calls, threading macros...).
Have also a look at the convenient pez/taplet that allows quickly tapping a whole let-binding vector.
spyscope provides several utilities similar to icecream but in the form of reader tags instead of macros.
Notably #spy/d
("details") is very close in behavior to ic
, including context resolution.
It also provides #spy/t
("trace") that is an aggregated asynchronous version better suited for multi-threaded applications.
Its output function cannot be customized.
tupelo provides spyx that behaves very close to ic
but for which the output function (println
) can't be redefined.
It doesn't print caller info (*include-context*
config). tupelo nethertheless provides fn-info and fn-info-caller (both Clojure only) that is very close in implementation to icecream's get-call-context
.
hashp works just like spyscope's #spy/p
but includes partial context (call location in file).