Tracing & Logging
When a machine misbehaves, the first question is always “where is it, and how did it get there?” StateWalker ships a small tracing layer that answers this without editing a single handler: attach a tracer and watch the machine emit a readable stream of enter/exit markers as it moves.
Trace a whole process
setProcessTracer hooks onStateCreate and attaches a tracer to every state. Each state prints an XML-like tag on enter and a closing tag on exit, so nested states read as nested tags:
import { FsmProcess, setProcessTracer } from "@statewalker/fsm";
const process = new FsmProcess(config);
const stop = setProcessTracer(process); // returns a disposer
await process.dispatch("");
await process.dispatch("toggle");
<LightBulb event="">
<Off event="">
</Off> <!-- event="toggle" -->
<On event="toggle">
Call the returned disposer to stop tracing. Attach the tracer before you start dispatching so the initial transitions are captured.
Trace a single state
To watch just one branch, use setStateTracer from inside your own onStateCreate:
import { setStateTracer } from "@statewalker/fsm";
process.onStateCreate((state) => {
if (state.key === "Paying") setStateTracer(state);
});
Indented, prefixed output
By default trace lines go to console.log. Attach a printer to control the sink and the formatting. A printer indents each line by the current state depth — two spaces per level — so the log visually mirrors the state tree:
import { setProcessPrinter, setProcessTracer } from "@statewalker/fsm";
setProcessPrinter(process, {
prefix: "[fsm]", // prepended to every line
lineNumbers: true, // prepend an incrementing [n] counter
print: (...args) => myLogger.debug(...args), // defaults to console.log
});
setProcessTracer(process); // now traces through the printer
setProcessPrinter stores the printer in a weak map keyed by the process, so it is garbage-collected with the process and you never thread a logger through your handlers. The tracer picks it up automatically via getPrinter(state), which returns the state’s own printer if set, otherwise the process’s, otherwise console.log.
Passing your own sink
Every tracer accepts an explicit print argument that overrides the printer lookup — handy for capturing trace output into an array in a test:
const lines: string[] = [];
setProcessTracer(process, (...args) => lines.push(args.join(" ")));
Building custom log lines
The printer is exposed if you want to log your own messages with the same depth-aware indentation. preparePrinter(process, config) builds one; getProcessPrinter(process) retrieves the attached one:
import { getProcessPrinter } from "@statewalker/fsm";
const log = getProcessPrinter(process);
process.onStateCreate((state) => {
state.onEnter(() => log("entered", state.key));
});