Throwables as data, in two halves.
trace is the navigable half: a sequential, complete run of sites —
§3's second element type — so it reduces, filters and maps like any other
result in the library, and its elements feed nav/form:
(->> (exc/trace e) (filter :clj?) (map :sym) (map nav/form))
profile is the ocular half: what went wrong, pruned for reading, with a
trace attached. render turns a profile into a string. They are decoupled;
a profile is data, so the registry can store it and answer questions about
it later, which a string could not.
Throwables as data, in two halves.
`trace` is the navigable half: a sequential, complete run of **sites** —
§3's second element type — so it reduces, filters and maps like any other
result in the library, and its elements feed `nav/form`:
(->> (exc/trace e) (filter :clj?) (map :sym) (map nav/form))
`profile` is the ocular half: what went wrong, pruned for reading, with a
trace attached. `render` turns a profile into a string. They are decoupled;
a profile is data, so the registry can store it and answer questions about
it later, which a string could not.(causes e)The chain, outermost first and root last. Throwables rather than data, so
that trace and profile apply to each in turn.
Capped, because a self-referencing cause would otherwise hang the thing meant to explain the hang.
The chain, outermost first and root last. Throwables rather than data, so that `trace` and `profile` apply to each in turn. Capped, because a self-referencing cause would otherwise hang the thing meant to explain the hang.
(frame elem)One stack frame as a site. Clojure frames get a demunged ns/name symbol,
which by §3's invariant is a valid input to nav/form — so the source of a
frame becomes a lookup rather than a feature. Java frames get Class/method,
still a symbol a reader would produce, just one nothing will resolve.
One stack frame as a site. Clojure frames get a demunged `ns/name` symbol, which by §3's invariant is a valid input to `nav/form` — so the source of a frame becomes a lookup rather than a feature. Java frames get `Class/method`, still a symbol a reader would produce, just one nothing will resolve.
(here)The current stack as sites. Hand this to profile as the baseline and the
path the call arrived by is subtracted from what it reports.
The current stack as sites. Hand this to `profile` as the baseline and the path the call arrived by is subtracted from what it reports.
(profile e)(profile e baseline)What went wrong, for reading: the root cause at the top level, because
that is what actually failed, with wrappers under :via because those are
context.
Clojure wraps aggressively — a CompilerException around the real failure
is the common case — so a format reporting only the outermost message
describes the wrapper instead of the problem. :via is absent when nothing
wraps anything.
Pass baseline — see here — and the arrival path is subtracted, along
with the compiler scaffolding beneath it. :dropped reports how much, since
a pruned trace and a genuinely short one look identical otherwise, and the
difference matters when the answer is "the interesting frame was in the part
you hid".
What went wrong, for reading: the **root cause** at the top level, because that is what actually failed, with wrappers under `:via` because those are context. Clojure wraps aggressively — a `CompilerException` around the real failure is the common case — so a format reporting only the outermost message describes the wrapper instead of the problem. `:via` is absent when nothing wraps anything. Pass `baseline` — see `here` — and the arrival path is subtracted, along with the compiler scaffolding beneath it. `:dropped` reports how much, since a pruned trace and a genuinely short one look identical otherwise, and the difference matters when the answer is "the interesting frame was in the part you hid".
(render {:keys [type msg data via trace dropped]})A profile as a string, for ocular inspection. Grouping happens here, not in the data: §3 keeps results sequential so they compose, and puts rendering at exactly one boundary per pipeline.
A profile as a string, for ocular inspection. Grouping happens here, not in the data: §3 keeps results sequential so they compose, and puts rendering at exactly one boundary per pipeline.
(trace e)Every distinct call involved, deepest first: the root cause's sites, then whatever each wrapper adds that is not already accounted for.
Complete — nothing is pruned. Pruning is profile's job, because pruning
needs to know where the call came from and a trace does not.
Subtracting rather than concatenating, because a wrapper's trace is normally
an exact suffix of its cause's — catching and rethrowing on one thread adds
no frames, so mapcat over the chain would report everything twice. Across
threads the two are disjoint and both matter: the failure inside the task,
then the thread that was awaiting it. Sites contributed by anything but the
root carry :from, naming what rethrew them.
Every distinct call involved, deepest first: the root cause's sites, then whatever each wrapper adds that is not already accounted for. Complete — nothing is pruned. Pruning is `profile`'s job, because pruning needs to know where the call came *from* and a trace does not. Subtracting rather than concatenating, because a wrapper's trace is normally an exact suffix of its cause's — catching and rethrowing on one thread adds no frames, so `mapcat` over the chain would report everything twice. Across threads the two are disjoint and both matter: the failure inside the task, then the thread that was awaiting it. Sites contributed by anything but the root carry `:from`, naming what rethrew them.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |