Skip to content

Commit 91cde79

Browse files
committed
[doc] Update, expand help and docstrings
1 parent 357bfc4 commit 91cde79

File tree

7 files changed

+329
-0
lines changed

7 files changed

+329
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
Tufte supports extensive environmental config via JVM properties,
2+
environment variables, or classpath resources.
3+
4+
Environmental filter config includes:
5+
6+
1. Minimum level (see signal `:level`):
7+
a. JVM property: `taoensso.tufte.rt-min-level`
8+
b. Env variable: `TAOENSSO_TUFTE_RT_MIN_LEVEL`
9+
c. Classpath resource: `taoensso.tufte.rt-min-level`
10+
11+
2. Namespace filter (see signal `:ns`):
12+
a. JVM property: `taoensso.tufte.rt-ns-filter`
13+
b. Env variable: `TAOENSSO_TUFTE_RT_NS_FILTER`
14+
c. Classpath resource: `taoensso.tufte.rt-ns-filter`
15+
16+
3. Id filter (see signal `:id`):
17+
a. JVM property: `taoensso.tufte.rt-id-filter`
18+
b. Env variable: `TAOENSSO_TUFTE_RT_ID_FILTER`
19+
c. Classpath resource: `taoensso.tufte.rt-id-filter`
20+
21+
Config values are parsed as edn, examples:
22+
23+
`taoensso.tufte.rt-min-level` => ":info"
24+
`TAOENSSO_TUFTE_RT_NS_FILTER` => "{:disallow \"taoensso.*\"}"
25+
`taoensso.tufte.rt-id-filter.cljs` => "#{:my-id1 :my-id2}"
26+
`TAOENSSO_TUFTE_RT_ID_FILTER_CLJ` => "nil"
27+
28+
Runtime vs compile-time filters
29+
30+
The above filters (1..4) all apply at RUNTIME ("rt").
31+
This is typically what you want, since it allows you to freely adjust filtering
32+
(making it less OR MORE permissive) through later API calls like `set-min-level!`.
33+
34+
As an advanced option, you can instead/additionally ELIDE (entirely omit) filtered
35+
callsites at COMPILE-TIME ("ct") by replacing "rt"->"ct" / "RT"->"CT" in the config
36+
ids above. Compile-time filters CANNOT be made MORE permissive at runtime.
37+
38+
Tips:
39+
40+
- The above config ids will affect both Clj AND Cljs.
41+
For platform-specific filters, use
42+
".clj" / "_CLJ" or
43+
".cljs" / "_CLJS" suffixes instead.
44+
e.g. "taoensso.tufte.rt-min-level.cljs".
45+
46+
- To get the right edn syntax, first set your runtime filters using the
47+
standard utils (`set-min-level!`, etc.). Then call `get-filters` and
48+
serialize the relevant parts to edn with `pr-str`.
49+
50+
- All environmental config uses `get-env` underneath.
51+
See the `get-env` docstring for more/advanced details.
52+
53+
- Classpath resources are files accessible on your project's
54+
classpath. This usually includes files in your project's
55+
`resources/` dir.

resources/docs/filters.txt

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
Tufte profiling is activated using the `profiled` or `profile` macros:
2+
3+
- `profiled` returns [<body-result> <?pstats>].
4+
Handy when you want to consume profiling results directly at the callsite.
5+
6+
- `profile` returns <body-result> and dispatches a profiling signal (map)
7+
to all registered handlers. This map includes `:pstats` and other info.
8+
9+
Handy when you want to consume profiling results later/elsewhere.
10+
11+
Profiling is activated only when ALL of the following are true:
12+
13+
1. Call filters pass (relevant for both `profile/d`):
14+
a. Compile-time: sample rate, kind, ns, id, level, when form, rate limit
15+
b. Runtime: sample rate, kind, ns, id, level, when form, rate limit
16+
17+
2. Handler filters pass (relevant only for `profile`):
18+
a. Compile-time: not applicable
19+
b. Runtime: sample rate, kind, ns, id, level, when fn, rate limit
20+
21+
(Relevant only for `profile`):
22+
3. Call transform (fn [signal]) => ?modified-signal returns non-nil
23+
4. Handler transform (fn [signal]) => ?modified-signal returns non-nil
24+
25+
Transform fns provides a flexible way to modify and/or filter signals by
26+
arbitrary signal data/content conditions (return nil to skip handling).
27+
28+
Config:
29+
30+
To set call filters (1a, 1b):
31+
32+
Use:
33+
`set-kind-filter!`, `with-kind-filter`
34+
`set-ns-filter!`, `with-ns-filter`
35+
`set-id-filter!`, `with-id-filter`
36+
`set-min-level!`, `with-min-level`
37+
38+
or see `help:environmental-config`.
39+
40+
To set handler filters (2b) or transform (4):
41+
42+
Provide relevant opts when calling `add-handler!` or `with-handler/+`.
43+
See `help:handler-dispatch-options` for details.
44+
45+
Note: call filters (1a, 1b) should generally be AT LEAST as permissive
46+
as handler filters (2b) since they're always applied first.
47+
48+
To set call transform (3): use `set-xfn!`, `with-xfn`.
49+
50+
Compile-time vs runtime filtering:
51+
52+
Compile-time filters are an advanced feature that can be tricky to set
53+
and use correctly. Most folks will want ONLY runtime filters.
54+
55+
Compile-time filters works by eliding (completely removing the code for)
56+
disallowed calls. This means zero performance cost for these calls, but
57+
also means that compile-time filters are PERMANENT once applied.
58+
59+
So if you set `:info` as the compile-time minimum level, that'll REMOVE
60+
CODE for every signal call below `:info` level. To decrease that minimum
61+
level, you'll need to rebuild.
62+
63+
Compile-time filters can be set ONLY with environmental config
64+
(see `help:environmental-config` for details).
65+
66+
Signal and handler sampling is multiplicative:
67+
68+
Both calls and handlers can have independent sample rates, and these
69+
MULTIPLY! If a signal is created with 20% sampling and a handler
70+
handles 50% of received signals, then 10% of possible signals will be
71+
handled (50% of 20%).
72+
73+
When sampling is active, the final (combined multiplicative) rate is
74+
helpfully reflected in each signal's `:sample` rate value ∈ℝ[0,1].
75+
76+
If anything is unclear, please ping me (@ptaoussanis) so that I can
77+
improve these docs!

resources/docs/p.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Profiling spy.
2+
3+
Use this macro to wrap forms that should be timed during active profiling.
4+
A unique form id (keyword) must be provided, e.g.:
5+
6+
(p ::my-form (do-something))
7+
(p {:id ::my-form} (do-something))
8+
(p {:id ::my-form
9+
:level :debug} (do-something))
10+
11+
`p` will ALWAYS execute its body and return the body's result, even when
12+
profiling isn't active.
13+
14+
Options include:
15+
`:id` ---- Unique id for this form in resulting `pstats` (e.g. `::my-fn-call`)
16+
`:level` - Profiling level ∈ #{<int> :trace :debug :info :warn :error :fatal :report ...}
17+
(default `:info`)

resources/docs/profile.txt

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
Use this to conditionally activate profiling for given body:
2+
3+
- ALWAYS executes body and returns <body-result>.
4+
- When filtering conditions are met (see `help:filters`), records execution times
5+
of all `p` forms in body and dispatches a profiling signal map (see `help:signal-content`)
6+
to any registered handlers (see `help:handlers`).
7+
8+
Handy when you want to consume profiling results asynchronously and/or away from
9+
the callsite, otherwise prefer `profile`.
10+
11+
Options include:
12+
13+
`:dynamic?` --- Perform multi-threaded profiling with binding conveyance? (default false)
14+
`:level` ------ Profiling level (default `:info`), must be >= active minimum level to profile
15+
`:id` --------- Profiling ?id for filtering, etc. (e.g. `::my-profiling-id`)
16+
17+
`:data` ------- Arb app-level ?data to incl. in signal: usu. a map
18+
`:ctx` -------- Custom ?val to override auto (dynamic `*ctx*`) in signal, as per `with-ctx`
19+
`:ctx+` ------- Custom ?val to update auto (dynamic `*ctx*`) in signal, as per `with-ctx+`
20+
`:xfn` -------- Optional transform (fn [signal]) => ?modified-signal to apply when signal is created, as per `with-xfn`
21+
`:xfn+` ------- Optional extra transform (fn [signal]) => ?modified-signal to apply when signal is created, as per `with-xfn+`
22+
23+
`:sample` ----- Sample rate ∈ℝ[0,1], profile only this random proportion of calls (0.75 => 75%, nil => all)
24+
`:when` ------- Arb ?form; when present, form must return truthy to allow profiling
25+
`:limit` ------ Rate limit ?spec given to `taoensso.tufte/rate-limiter`, see its docstring for details
26+
`:limit-by` --- When present, rate limits will be enforced independently for each value (any Clojure value!)
27+
28+
`:nmax` ------- Max captures per `p` id before compaction (default 8e5).
29+
`:elidable?` -- Should signal be subject to compile-time elision? (default true)
30+
31+
Laziness in body:
32+
33+
Lazy seqs and other forms of laziness (e.g. delays) in body will only
34+
contribute to profiling results if/when EVALUATION ACTUALLY OCCURS.
35+
This is intentional and a useful property. Compare:
36+
37+
(profile {} (delay (Thread/sleep 2000))) ; Doesn't count sleep
38+
(profile {} @(delay (Thread/sleep 2000))) ; Does count sleep
39+
40+
Async code in body:
41+
42+
Execution time of any code in body that runs asynchronously on a
43+
different thread will generally NOT be automatically captured by default.
44+
45+
`:dynamic?` can be used to support capture in cases where Clojure's
46+
binding conveyance applies (e.g. futures, agents, pmap). Just make sure
47+
that all work you want to capture has COMPLETED before the `profile`
48+
form ends- for example, by blocking on pending futures.
49+
50+
In other advanced cases (notably core.async `go` blocks), please see
51+
`with-profiling` and `capture-time!`.
52+
53+
`core.async` warning:
54+
55+
`core.async` code can be difficult to profile correctly without a deep
56+
understanding of precisely what it's doing under-the-covers.
57+
58+
Some general recommendations that can help keep things simple:
59+
60+
- Try minimize the amount of code + logic in `go` blocks. Use `go`
61+
blocks for un/parking to get the data you need, then pass the data
62+
to external fns. Profile these fns (or in these fns), not in your
63+
`go` blocks.
64+
65+
- In particular: you MUST NEVER have parking calls inside
66+
`(profile {:dynamic? false} ...)`.
67+
68+
This can lead to concurrency exceptions.
69+
70+
If you must profile code within a go block, and you really want to
71+
include un/parking times, use `(profile {:dynamic? true} ...)`
72+
instead.

resources/docs/profiled.txt

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
Use this to conditionally activate profiling for given body:
2+
3+
- ALWAYS executes body and returns [<body-result> <?pstats>].
4+
- When filtering conditions are met (see `help:filters`), records execution times
5+
of all `p` forms in body and includes resulting `pstats` object in return value.
6+
7+
Handy when you want to consume profiling results directly at the callsite,
8+
otherwise prefer `profile`.
9+
10+
Options include:
11+
12+
`:dynamic?` --- Perform multi-threaded profiling with binding conveyance? (default false)
13+
`:level` ------ Profiling level (default `:info`), must be >= active minimum level to profile
14+
`:id` --------- Profiling ?id for filtering, etc. (e.g. `::my-profiling-id`)
15+
16+
`:sample` ----- Sample rate ∈ℝ[0,1], profile only this random proportion of calls (0.75 => 75%, nil => all)
17+
`:when` ------- Arb ?form; when present, form must return truthy to allow profiling
18+
`:limit` ------ Rate limit ?spec given to `taoensso.tufte/rate-limiter`, see its docstring for details
19+
`:limit-by` --- When present, rate limits will be enforced independently for each value (any Clojure value!)
20+
21+
`:nmax` ------- Max captures per `p` id before compaction (default 8e5).
22+
`:elidable?` -- Should profiling be subject to compile-time elision? (default true)
23+
24+
Laziness in body:
25+
26+
Lazy seqs and other forms of laziness (e.g. delays) in body will only
27+
contribute to profiling results if/when EVALUATION ACTUALLY OCCURS.
28+
This is intentional and a useful property. Compare:
29+
30+
(profiled {} (delay (Thread/sleep 2000))) ; Doesn't count sleep
31+
(profiled {} @(delay (Thread/sleep 2000))) ; Does count sleep
32+
33+
Async code in body:
34+
35+
Execution time of any code in body that runs asynchronously on a
36+
different thread will generally NOT be automatically captured by default.
37+
38+
`:dynamic?` can be used to support capture in cases where Clojure's
39+
binding conveyance applies (e.g. futures, agents, pmap). Just make sure
40+
that all work you want to capture has COMPLETED before the `profiled`
41+
form ends- for example, by blocking on pending futures.
42+
43+
In other advanced cases (notably core.async `go` blocks), please see
44+
`with-profiling` and `capture-time!`.
45+
46+
`core.async` warning:
47+
48+
`core.async` code can be difficult to profile correctly without a deep
49+
understanding of precisely what it's doing under-the-covers.
50+
51+
Some general recommendations that can help keep things simple:
52+
53+
- Try minimize the amount of code + logic in `go` blocks. Use `go`
54+
blocks for un/parking to get the data you need, then pass the data
55+
to external fns. Profile these fns (or in these fns), not in your
56+
`go` blocks.
57+
58+
- In particular: you MUST NEVER have parking calls inside
59+
`(profiled {:dynamic? false} ...)`.
60+
61+
This can lead to concurrency exceptions.
62+
63+
If you must profile code within a go block, and you really want to
64+
include un/parking times, use `(profiled {:dynamic? true} ...)`
65+
instead.

resources/docs/pstats-content.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Profiling stats (`pstats`)
2+
3+
When profiling (activated by `profile/d`) is complete, you'll get back a
4+
profiling stats (`pstats`) object that can be derefed and/or merged
5+
with other `pstats`:
6+
7+
- @pstats => {:clock {:keys [t0 t1 total]}, :stats {<id> {:keys [n sum ...]}}}
8+
- @(merge-pstats ps1 ps2) => {:clock {:keys [t0 t1 total]}, :stats {<id> {:keys [n sum ...]}}}
9+
10+
Full set of keys in the above `:stats` maps:
11+
:n :min :max :mean :mad :sum :p25 :p50 :p75 :p90 :p95 :p99 :loc :last
12+
13+
All values are numerical (longs or doubles), except for `:loc` which is
14+
a map of `p` callsite location information, or set of such maps, e.g.:
15+
#{{:ns \"my-ns\", :line 122, :column 21}}

resources/docs/signal-content.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
Tufte profiling signals are maps with {:keys [inst id ns level data ...]},
2+
though they can be modified by call and/or handler transforms (xfns).
3+
4+
Default signal keys:
5+
6+
`:schema` ----------- Int version of signal schema (current: 1)
7+
`:inst` ------------- Platform instant [1] when `profile` called, monotonicity depends on system clock
8+
`:ns` --------------- ?str namespace of `profile` callsite
9+
`:coords` ----------- ?[line column] of `profile` callsite
10+
11+
`:level` ------------ Profiling level ∈ #{<int> :trace :debug :info :warn :error :fatal :report ...}
12+
`:id` --------------- Profiling ?id (usu. keyword)
13+
14+
`:data` ------------- Arb app-level data ?val (usu. a map) given to `profile` call
15+
`:ctx` -------------- ?val of `*ctx*` (arb app-level state) when `profile` was called
16+
17+
`:body-result` ------ Return value of the body wrapped by `profile` call
18+
`:pstats` ----------- Profiling stats object that can be derefed and/or merged (see `help:pstats-content`)
19+
`:format-pstats-fn` - Cached (fn [pstats]) => formatted table string function
20+
21+
`:host` ------------- (Clj only) {:keys [name ip]} info for network host
22+
`:thread` ----------- (Clj only) {:keys [name id group]} info for thread that called `profile`
23+
24+
`:sample` ----------- Sample ?rate ∈ℝ[0,1] for combined call AND handler sampling (0.75 => allow 75% of signals, nil => allow all)
25+
26+
If anything is unclear, please ping me (@ptaoussanis) so that I can improve these docs!
27+
28+
[1] `java.time.Instant` or `js/Date`

0 commit comments

Comments
 (0)