Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

README.md

logs

A toolbox. Its tools read what the engine logged.

Everything the engine records lands in one in-memory ring — Rust log lines, bridged renderer and asset errors from third-party crates, Rust panics, and every print and log.* a script made. These tools read it back from inside the engine, on every platform — a browser has no host filesystem to reach for and no console to scroll.

The tools

  • search — the one to reach for. Filters by severity, subsystem, substring or regex, the script or entity that logged a line, and a time window. Can return the lines around each match, or collapse a repeating message into a single row.
  • tail — the most recent lines of any level, oldest first. What is happening right now.
  • errors — recent errors and warnings, newest first.
  • summary — how many lines, of what severity, from which subsystems and entities, and which messages repeat. The shape of the noise before you go looking inside it.
  • clear — drops what is buffered. Lifetime counts survive.

The ring as files

The same ring is mounted read-only on the VFS — the engine's own filesystem, which is why these need no host disk behind them — and cat and grep work on them like any other file:

PathWhat it holds
/zero/runtime/logs/engineevery level, most recent 500 lines
/zero/runtime/logs/errorsthe ERROR-and-above subset of the same
/zero/runtime/logs/scriptwhat scripts logged
/zero/runtime/logs/previousthe previous session of this world: how it ended, then the last lines it wrote

The ring starts empty at every boot, so it cannot say why the engine before this one stopped. previous can: its first line names how the previous session of the world this engine booted on ended (a clean shutdown and its reason, an exit with no reason recorded, or no exit record at all because the process was killed or faulted), with the panic and the crash record that explain an ending that was not clean, and the lines that session wrote follow. Those lines are as the engine wrote them to its log file, each opening with its [seconds.millis] stamp, where the live engine and errors files open each line with its number in this session's ring. A boot whose previous session ended without a clean shutdown also says so in a notice and a WARN line. Each engine on the machine keeps its own record of each world, so an engine that comes back reads the session it ran last, whatever other engines on the same world did meanwhile.

Neither surface is the right one — they answer different questions. A message you can already name is quicker to grep than to query. A question with a shape to it — this entity's lines, this severity in this window, what surrounded the failure, which message repeats and how often — is what the tools are for, and a file cannot answer it. The files also stop at 500 lines and include the record of your own tool calls, so grepping for a marker string can return the call that went looking for it; a query reaches the whole ring and leaves those out unless include_mcp asks for them.

Finding out why something misbehaved

The problems tool in the debug toolbox is the first call when something you built does not behave: it drains the errors and warnings you have not read yet, oldest first, so the root-cause failure reads ahead of the cascade it set off.

This toolbox is the general view over the same ring — every level, searchable, countable, and filterable by origin and by caller. Reach for it when you need more than the unread problems: a specific message, a specific entity, a specific minute, one agent's failures on an engine several of them share, or the lines that led up to a failure.

What a line carries

fieldmeaning
seqmonotonic, unique for the process lifetime
levelTRACE, DEBUG, INFO, WARN, ERROR
typesubsystem — RENDERER, SCRIPT, PHYSICS, UI, …
messagethe line itself
timestampepoch seconds
originlocal, or remote for a line raised by content another session on this world authored
callerwhich caller's work raised it, as the key the caller answers by on the engine that raised it (agent:<name> while the caller holds a name, else client:<session> / actor:<sub>), on the lines a caller's work raised
scriptthe script that logged it, when a script did
entitythe entity that script was running on, when there was one
contextpresent only on a line returned as context around another line's match

script and entity are why asking for one entity's output is a filter rather than a guess at what its messages look like.

caller is the same move one axis over: one engine answers several agents at once and runs all of their content, so its log holds all of their failures. Pass caller = "mine" for your own lines, "unowned" for the engine's own, or a caller key spelled out for somebody else's.

Reading incrementally

summary reports a cursor — the highest sequence number the ring has assigned. Read it before an action and hand it back to search as since afterwards, and you see only what that action logged. engine_cursor is the same figure with your own tool traffic left out, so it stands still on an engine whose loop has stopped however many times you poll it.

Your own tool traffic

Each tool call is itself recorded, with type of MCP. A search leaves those out, because the call carrying the search is one of them and an unqualified search for a marker would otherwise return the search. include_mcp sees them alongside everything else; a type of MCP sees only them.