11 — Instrumentation¶
The instrumentation surface is two surfaces stacked. The first is dev-only: a trace bus that emits one richly-tagged record per noteworthy event (dispatch, sub recompute, fx walk, render, machine transition, schema validation, error), buffered into a ring, fanned out to registered listeners synchronously, and elided entirely under :advanced + goog.DEBUG=false. The second is always-on: a pair of tight, production-survivable substrates (event-emit, error-emit) that deliver one record per processed event and one record per :rf.error/* event. Together they let the same app feed Causa in dev and Sentry / Datadog / Honeybadger in production from the same registration.
This is the load-bearing surface for the pair-shape architecture — every tool that watches a running re-frame2 app composes against one of these surfaces. Causa subscribes to the dev trace bus. The MCP servers do the same. The event-emit substrate is what hosted observability shippers consume. The error-emit substrate is what hosted error monitors consume. Same registrations, three audiences.
This chapter covers the event-emit listener surface, the error-emit listener surface, the dev-only tracing surface, the epoch buffer (time-travel), the performance instrumentation gate, the source-coord annotation contract, the wire-boundary elision walker, and the error contract.
Event-emit (always-on, production-survivable)¶
A minimal always-on listener surface that survives :advanced + goog.DEBUG=false and delivers one tight record per processed event. The intended consumers are hosted observability back-ends (Datadog, Honeycomb, Sentry, …). Parallel to (not a fallback for) the dev-only trace surface; per-event only — no per-sub, per-fx, or per-:rf.event/db-changed records.
Record shape: {:event :event-id :frame :time :outcome :elapsed-ms}. The :event slot is passed through elide-wire-value (see below) once before fan-out, so schema-marked :sensitive? paths land as :rf/redacted and :large? paths land as :rf.size/large-elided.
register-event-listener!¶
- Kind: function
- Signature:
- Description: Receive one event-record per processed event. Re-registering the same
idreplaces. Returnsid. Always-on: survives CLJS:advanced+goog.DEBUG=false.
unregister-event-listener!¶
- Kind: function
- Signature:
- Description: The inverse.
Error-emit (always-on, production-survivable)¶
Sibling of the event-emit surface above. Runs through the SAME always-on error-emit substrate as the per-frame :on-error slot (002-Frames.md) but along an INDEPENDENT corpus-wide fan-out path. Survives :advanced + goog.DEBUG=false. Intended consumers are hosted error monitors (Sentry, Honeybadger, Rollbar).
Record shape: {:error :event :event-id :frame :time :exception :elapsed-ms}. The :event slot is passed through elide-wire-value once before fan-out (same redaction posture as event-emit). The two paths from the substrate (corpus-wide listeners AND the per-frame :on-error policy fn) are mutually isolated; either may throw without affecting the other.
register-error-listener!¶
- Kind: function
- Signature:
- Description: Receive one error-record per
:rf.error/*event. Re-registering the sameidreplaces. Returnsid. Always-on: survives CLJS:advanced+goog.DEBUG=false.
unregister-error-listener!¶
- Kind: function
- Signature:
- Description: The inverse.
Tracing (dev-only)¶
The rich-detail trace surface. Dev-only — elided in production via Closure DCE under :advanced + goog.DEBUG=false. See 009 §Tracing for emit semantics and synchronous listener delivery.
register-listener!¶
- Kind: function
- Signature:
- Description: "Receive every trace event the runtime emits." Synchronous delivery; the callback returns before the next trace event is processed.
unregister-listener!¶
- Kind: function
- Signature:
- Description: The inverse.
emit-trace-event!¶
- Kind: function
- Signature:
- Description: "Emit a custom trace event." Use sparingly — the framework emits the load-bearing events; custom emission is for app-specific cross-cutting concerns the framework can't know about.
re-frame.interop/debug-enabled?¶
- Kind: Var (
^boolean) - Description: CLJS: alias of
goog.DEBUG— constant-folded by Closure under:advanced, so:advanced+goog.DEBUG=falsebuilds DCE every(when interop/debug-enabled? ...)branch. JVM: adefread ONCE at ns-load from the Java system property-Dre-frame.debug(winning on conflict) or the environment variableRE_FRAME_DEBUG; defaultstrue(dev parity). Accepts the conventional false-y vocabulary case-insensitively (false,0,no,off, empty string) with whitespace trimmed; anything else leaves the flag attrue. SSR / webhook receivers / long-running JVMs facing untrusted input MUST set the gatefalseexplicitly.
re-frame.performance/enabled?¶
- Kind: Var (
^boolean) - Description:
goog-defined (CLJS) /^:const false(JVM). Set via:closure-defines {re-frame.performance/enabled? true}to bracket event dispatch / sub recompute / fx walk / view render inperformance.mark+performance.measurecalls (User-Timing entriesrf:event:*,rf:sub:*,rf:fx:*,rf:render:*). Compile-time only — not a(rf/configure ...)knob; runtime mutation has no effect. Defaultfalse; under:advanced+ default the bracket DCEs and shipped binaries carry zero User-Timing instrumentation. CLJS-only — JVM is a no-op.
trace-buffer¶
- Kind: function
- Signature:
- Description: "What's in the ring right now?" Reads the buffer non-destructively. Pair tools and Causa use this for post-mortem inspection.
clear-trace-buffer!¶
- Kind: function
- Signature:
- Description: Empty the ring.
(rf/configure :trace-buffer …)¶
- Kind: config key
- Signature:
- Description: Buffer depth knob. See 01 — Core §Configure keys.
group-cascades¶
- Kind: function
- Signature:
- Description: Pure data projection of a list of trace events into per-cascade records
{:dispatch-id :event :handler :fx :effects :subs :renders :other}, sorted by emission order. JVM-runnable. Re-exported fromre-frame.trace.projection.
domino-bucket¶
- Kind: function
- Signature:
- Description: Classify a raw trace event into the six-domino slot used by
group-cascades. Pure.
Trace-emission opt-out¶
Event-handler registration accepts a :rf.trace/no-emit? true metadata flag. When set, the runtime suppresses every trace emission and event-emit record within the handler's scope — the handler runs invisibly to the trace surface, the event-emit substrate, and (transitively) the epoch buffer.
| Metadata key | Where | Value | Default | Effect |
|---|---|---|---|---|
:rf.trace/no-emit? |
reg-event-db / reg-event-fx / reg-event-ctx metadata map |
boolean | false |
When true, suppresses all trace + event-emit emissions inside the handler's scope. |
Used by framework-internal bookkeeping handlers (Causa, Story, re-frame2-pair-mcp, story-mcp) that would otherwise saturate the trace stream. The :rf.trace/* namespace is framework-owned (per Conventions §Reserved namespaces).
Epoch history (Tool-Pair)¶
Per-frame epoch snapshots, recorded on each drain-completion in dev builds. Used by pair-shaped tools for time-travel and post-mortem analysis. Production builds elide entirely.
epoch-history¶
- Kind: function
- Signature:
- Description: Returns
[]for an unknown / destroyed frame.
restore-epoch¶
- Kind: function
- Signature:
- Description: Restore the frame's
app-dbto the named epoch. Returnstrueon success;falsefor an unknown / destroyed frame (and emits:rf.error/no-such-handlerof kind:frame).
reset-frame-db!¶
- Kind: function
- Signature:
- Description: Pair-tool write surface (state injection). Direct write to
app-db— bypasses the cascade. Returnstrueon success.
register-epoch-listener!¶
- Kind: function
- Signature:
- Description: Process-global assembled-epoch listener. A callback whose previously-observed frame is destroyed receives a one-shot
:rf.epoch.cb/silenced-on-frame-destroytrace.
unregister-epoch-listener!¶
- Kind: function
- Signature:
- Description: The inverse.
(rf/configure :epoch-history …)¶
- Kind: config key
- Signature:
- Description: Buffer-depth and redactor knobs. See 01 — Core §Configure keys.
Trace events emitted by epoch-history machinery¶
:operation |
Tags |
|---|---|
:rf.epoch/snapshotted |
:frame, :epoch-id, :event-id |
:rf.epoch/restored |
:frame, :epoch-id |
:rf.epoch/db-replaced |
:frame, :epoch-id |
:rf.epoch/restore-unknown-epoch |
:frame, :epoch-id, :history-size |
:rf.epoch/restore-schema-mismatch |
:frame, :epoch-id, :schema-digest-recorded, :schema-digest-current, :failing-paths |
:rf.epoch/restore-missing-handler |
:frame, :epoch-id, :missing |
:rf.epoch/restore-version-mismatch |
:frame, :epoch-id, :machine-id, :version-recorded, :version-current |
:rf.epoch/restore-during-drain |
:frame, :epoch-id |
:rf.epoch/restore-non-ok-record |
:frame, :epoch-id, :outcome, :halt-reason |
:rf.epoch/reset-frame-db-during-drain |
:frame |
:rf.epoch/reset-frame-db-schema-mismatch |
:frame, :failing-paths |
:rf.epoch.cb/silenced-on-frame-destroy |
:frame, :cb-id |
The wire-boundary walker¶
elide-wire-value is the framework primitive that walks tree-shaped values at the wire boundary and substitutes elision markers for sensitive or large slots. This walker is the single normative emission site for the :rf/redacted sensitive sentinel and the :rf.size/large-elided size marker. Per-tool reimplementation is prohibited.
elide-wire-value¶
- Kind: function
- Signature:
- Description: Walk
vconsulting[:rf/elision :declarations]and[:rf/elision :sensitive-declarations]of the named frame'sapp-db. Substitute:rf/redactedfor sensitive slots and:rf.size/large-elidedmarkers for large slots.
elision-declarations¶
- Kind: function
- Signature:
- Description: Read the current
[:rf/elision :declarations]map for the frame (or{}). Pair-tool / introspection reader.
populate-elision-from-schemas!¶
- Kind: function
- Signature:
- Description: Boot-time hydrator that walks the frame's registered app-schemas and writes
{:large? true :source :schema}declarations for every path whose Malli schema carries:large? true. Idempotent.
Composition rule: when both predicates match (sensitive AND large for the same path), sensitive drop wins — the size marker is suppressed because it would leak :path / :bytes / :digest from a sensitive slot.
See 08 — Schemas for the registration side (add-marks, set-marks, reg-app-schema with :sensitive? / :large? flags).
Privacy predicate¶
sensitive?¶
- Kind: function
- Signature:
- Description: True iff
trace-eventis a map carrying:sensitive? trueat the top level (not under:tags). The framework-published predicate every consumer composes against — replaces per-consumer reimplementations of the same five-token check.
redact-interceptor¶
- Kind: function
- Signature:
- Description: Positional interceptor that overwrites the named keys in the event vector's payload map with the
:rf/redactedsentinel before the handler chain runs. The handler body itself sees the UNREDACTED payload via the regular:eventcoeffect slot; the redaction is for the trace surface only.
DOM source-coord annotations¶
Every adapter whose host has a DOM-attribute concept (Reagent / UIx / Helix on the browser) injects data-rf2-source-coord="<ns>:<sym>:<line>:<col>" on the rendered root DOM element of each registered view. Format and exemptions live in Spec 006 §Source-coord annotation.
The annotation is gated on interop/debug-enabled? (the CLJS mirror of goog.DEBUG); production :advanced builds elide the attribute via dead-code elimination — there is no DOM-bytes cost in shipped bundles. The JVM SSR emitter mirrors the contract per Spec 011 §Source-coord annotation under SSR.
The error contract¶
Errors are emitted as structured trace events with :op-type :error (or :warning / :info / :fx / :flow / :frame) and a per-category :operation keyword. The complete normative catalogue — every :rf.error/*, :rf.warning/*, :rf.fx/*, :rf.cofx/*, :rf.ssr/*, :rf.epoch/*, :rf.flow/*, :rf.http/*, :rf.http.interceptor/*, :rf.frame/*, and :rf.route.nav-token/* event the runtime emits — lives at 009 §Error event catalogue (single source of truth for category names, :op-type discriminator, trigger conditions, default :recovery, and :tags payload keys).
Per-category Malli :tags schemas are canonicalised at Spec-Schemas §Per-category :tags schemas — one schema per catalogue row.
See also¶
- 01 — Core — the
:trace-buffer,:epoch-history,:elisionconfigure keys. - 08 — Schemas — the registration side of the elision posture.
- Spec 009 — Instrumentation — the normative source.
- Tool-Pair — how the epoch buffer, trace bus, and source-coord annotations compose into the pair-shaped tools.