|
4 | 4 |
|
5 | 5 | ===== Problem
|
6 | 6 |
|
7 |
| -You need to trace the behavior of your code. |
| 7 | +You want to trace the execution of your code, in order to see what it is doing. |
8 | 8 |
|
9 | 9 | ===== Solution
|
10 | 10 |
|
11 |
| -Use +clojure.tools.trace/trace+ to trace a value. |
12 |
| -[source,clojure] |
13 |
| ----- |
14 |
| -(use 'clojure.tools.trace) |
15 |
| -(trace (+ 2 2)) |
16 |
| -;; TRACE: 4 |
17 |
| -;; 4 |
18 |
| ----- |
| 11 | +Use +clojure.tools.trace/trace+ to print a value and return it. |
19 | 12 |
|
20 |
| -When tracing a value you can assign a tag to a trace to give additional information. |
21 | 13 | [source,clojure]
|
22 | 14 | ----
|
23 |
| -(def add-tag "The point of the addition") |
24 |
| -(trace add-tag (+ 2 2)) |
25 |
| -;; TRACE The point of the addition: 4 |
26 |
| -;; 4 |
| 15 | +(ns cookbook.core |
| 16 | + (:use clojure.tools.trace)) |
| 17 | +
|
| 18 | +(doall (map #(* 2 (trace %)) (range 10))) |
| 19 | +;; -> (0 2 4 6 8 10 12 14 16 18) |
| 20 | +;; *out* |
| 21 | +;; TRACE: 0 |
| 22 | +;; TRACE: 1 |
| 23 | +;; TRACE: 2 |
| 24 | +;; TRACE: 3 |
| 25 | +;; TRACE: 4 |
| 26 | +;; TRACE: 5 |
| 27 | +;; TRACE: 6 |
| 28 | +;; TRACE: 7 |
| 29 | +;; TRACE: 8 |
| 30 | +;; TRACE: 9 |
27 | 31 | ----
|
28 | 32 |
|
29 |
| -Use +trace-forms+ to trace a combination of forms. |
| 33 | +Use tags to give additional context to the output. |
30 | 34 |
|
31 | 35 | [source,clojure]
|
32 | 36 | ----
|
33 |
| -(trace-forms (+ 1 2 (/ 6 2))) |
| 37 | +(defn add |
| 38 | + [a b] |
| 39 | + (+ (trace "a" a) (trace "b" b))) |
| 40 | +
|
| 41 | +(add 2 4) |
34 | 42 | ;; -> 6
|
| 43 | +;; *out* |
| 44 | +;; TRACE a: 2 |
| 45 | +;; TRACE b: 4 |
35 | 46 | ----
|
36 |
| -Wait, nothing special happens. Lets try that again with some code that fails. |
| 47 | + |
| 48 | +Use +trace-forms+ to trace a series of expressions, some of which may |
| 49 | +fail. |
| 50 | + |
37 | 51 | [source,clojure]
|
38 | 52 | ----
|
39 | 53 | (trace-forms (+ 1 2 (/ 6 0)))
|
40 |
| -;;ArithmeticException Divide by zero |
41 |
| -;; Form failed: (/ 6 0) |
42 |
| -;; Form failed: (+ 1 2 (/ 6 0)) |
43 |
| -;; clojure.lang.Numbers.divide (Numbers.java:156) |
| 54 | +;; -> (throws an exception...) |
| 55 | +;; *out* |
| 56 | +;; ArithmeticException Divide by zero |
| 57 | +;; Form failed: (/ 6 0) |
| 58 | +;; Form failed: (+ 1 2 (/ 6 0)) |
| 59 | +;; clojure.lang.Numbers.divide (Numbers.java:156) |
44 | 60 | ----
|
45 |
| -With a form failing you now get a trace following the execution of the forms. |
46 | 61 |
|
47 |
| -Use +deftrace+ to define a traced function. |
| 62 | +If no exception occurs, `trace-forms` produces no output. |
| 63 | + |
| 64 | +Use `deftrace` in place of `defn` to define a traced function. |
| 65 | + |
48 | 66 | [source,clojure]
|
49 | 67 | ----
|
50 |
| -(deftrace divider [x y] (/ x y)) |
51 |
| -(divider 2 (+ 1 1)) |
52 |
| -;; TRACE t1467: (divider 2 2) |
| 68 | +(deftrace divide [x y] (/ x y)) |
| 69 | +(divide 2 (+ 1 1)) |
| 70 | +;; -> 1 |
| 71 | +;; *out* |
| 72 | +;; TRACE t1467: (divide 2 2) |
53 | 73 | ;; TRACE t1467: => 1
|
54 |
| -;; 1 |
55 | 74 | ----
|
56 | 75 |
|
57 |
| -Use +trace-vars+ and +untrace-vars+ to enable or disable tracing of a function. |
| 76 | +Use +trace-vars+ and +untrace-vars+ to enable or disable tracing of an |
| 77 | +existing function. |
| 78 | + |
58 | 79 | [source,clojure]
|
59 | 80 | ----
|
60 |
| -(defn adder [x y] (+ x y)) |
61 |
| -(trace-vars cookbook.core/adder) |
62 |
| -(adder 2 2) |
63 |
| -;; TRACE t1309: (cookbook.core/adder 2 2) |
| 81 | +(defn add [x y] (+ x y)) |
| 82 | +(trace-vars cookbook.core/add) |
| 83 | +(add 2 2) |
| 84 | +;; -> 4 |
| 85 | +;; *out* |
| 86 | +;; TRACE t1309: (cookbook.core/add 2 2) |
64 | 87 | ;; TRACE t1309: => 4
|
65 |
| -;; 4 |
66 |
| -(untrace-vars cookbook.core/adder) |
67 |
| -(adder 2 2) |
68 |
| -;; 4 |
| 88 | +(untrace-vars cookbook.core/add) |
| 89 | +(add 2 2) |
| 90 | +;; -> 4 |
69 | 91 | ----
|
70 | 92 |
|
71 |
| -Use +trace-ns+ and +untrace-ns+ to enable or disable tracing of all functions in a namespace. |
| 93 | +Use +trace-ns+ and +untrace-ns+ to enable or disable tracing of all |
| 94 | +functions in a namespace. |
| 95 | + |
72 | 96 | [source,clojure]
|
73 | 97 | ----
|
74 |
| -(trace-ns (create-ns 'cookbook.core)) |
75 |
| -(adder 2 (divider 6 3)) |
76 |
| -;; TRACE t1276: (cookbook.core/divider 6 3) |
| 98 | +(trace-ns 'cookbook.core) |
| 99 | +(add 2 (divide 6 3)) |
| 100 | +;; -> 4 |
| 101 | +;; *out* |
| 102 | +;; TRACE t1276: (cookbook.core/divide 6 3) |
77 | 103 | ;; TRACE t1276: => 2
|
78 |
| -;; TRACE t1277: (cookbook.core/adder 2 2) |
| 104 | +;; TRACE t1277: (cookbook.core/add 2 2) |
79 | 105 | ;; TRACE t1277: => 4
|
80 |
| -;; 4 |
81 |
| -(untrace-ns (create-ns 'cookbook.core)) |
82 |
| -(adder 2 (divider 6 3)) |
83 |
| -;; 4 |
| 106 | +
|
| 107 | +(untrace-ns 'cookbook.core) |
| 108 | +(add 2 (divide 6 3)) |
| 109 | +;; -> 4 |
84 | 110 | ----
|
85 |
| -Note the use of +create-ns+ which will return the +Namespace+ instance given a namespace that already exists. |
86 | 111 |
|
87 | 112 | ===== Discussion
|
88 |
| -Trace functionality will usually work in two different ways, as a pass through decoration of values, as with +trace+ or by changing the root-binding of a +var+ such in the case of +trace-var+ and +trace-ns+. |
89 | 113 |
|
90 |
| -It is worth noting that the +clojure.tools.trace/trace+ function can be rebound. By doing that you can change how traces are done and to where. The default implementation will print to the standard output, but you can easily change that. |
91 |
| -[source,clojure] |
92 |
| ----- |
93 |
| -(ns clojure.tools.trace) |
94 |
| -(defn tracer [name value] |
95 |
| - (spit "trace.txt" |
96 |
| - (str name ": " value "\n") :append true)) |
97 |
| ----- |
98 |
| -With this definition of +trace+, any call made to +trace+ will add a line with the trace information to the "trace.txt" file. In this quick and easy way you have added logging of the traces, which sometimes might be sufficient instead of adding a dependency to a more sophisticated logging library. |
| 114 | +`tools.trace` provides two approaches to tracing the execution of |
| 115 | +code: explicitly tracing specific values, or through re-binding vars |
| 116 | +or entire namespaces. These low-level and high-level approaches are |
| 117 | +both effective tools for tracking down tough bugs. |
99 | 118 |
|
100 |
| -Note that if you during development re-define a traced function you also need to re-enable the tracing for that function. |
| 119 | +For example, tracing can be a useful tool when trying to get your head |
| 120 | +around a `reduce` operation: |
101 | 121 |
|
102 |
| -Tracing can be a useful tool when trying to get your head around a reduction. |
103 | 122 | [source,clojure]
|
104 | 123 | ----
|
105 |
| -(trace-vars cookbook.core/adder) |
106 |
| -(reduce adder [1 2 3]) |
107 |
| -;; TRACE t1338: (cookbook.core/adder 1 2) |
| 124 | +(trace-vars cookbook.core/add) |
| 125 | +(reduce add [1 2 3]) |
| 126 | +;; -> 6 |
| 127 | +;; *out* |
| 128 | +;; TRACE t1338: (cookbook.core/add 1 2) |
108 | 129 | ;; TRACE t1338: => 3
|
109 |
| -;; TRACE t1339: (cookbook.core/adder 3 3) |
| 130 | +;; TRACE t1339: (cookbook.core/add 3 3) |
110 | 131 | ;; TRACE t1339: => 6
|
111 |
| -;; 6 |
112 | 132 | ----
|
113 |
| -This will allow you to follow along in each step of the reduction. |
114 | 133 |
|
115 |
| -There is a time and a place for everything, of course, and production isn't the most appropriate environment for you to flex your new-found Clojure-tracing skills. Typical places you'll make use of tracing are during development or while diagnosing a hard-to-catch bug you've been able to reproduce locally, but not diagnose. Include the +core.trace+ library in your project's +:dev+ profile to make tracing available where appropriate. |
| 134 | +This allows you to follow each step of the reduction. |
| 135 | + |
| 136 | +CAUTION: When using `trace-vars` or `trace-ns`, the traced version of |
| 137 | +a function is lost when the namespace is reloaded and the underlying |
| 138 | +var is redefined. You must remember to re-establish the traced |
| 139 | +versions, if necessary, after reloading a namespace. |
| 140 | + |
| 141 | +It is not advisable to deploy production code with tracing in |
| 142 | +place. Tracing is most suited to development and debugging, |
| 143 | +particularly from the REPL. Include +tools.trace+ in your |
| 144 | +`project.clj`'s +:dev+ profile to make tracing available only to |
| 145 | +development tasks. |
116 | 146 |
|
117 | 147 | ===== See also
|
118 |
| -* See the documentation for https://github.com/clojure/tools.trace[+clojure.tools.trace+] |
| 148 | + |
| 149 | +* Documentation for https://github.com/clojure/tools.trace[+clojure.tools.trace+] |
0 commit comments