Logs¶
When the agent does something unexpected (a task stalls, a schedule doesn't fire, an action errors) the logs are the ground truth. Every run writes a timestamped folder under logs/ at the project root, capturing what every subsystem did, down to module and line number.
Overview¶
CraftBot logs with Loguru, and each process run gets one folder: logs/<timestamp>/ (e.g. logs/20260717085754/). Inside, the same stream is split three ways by who was speaking:
| File | Contains | Read it when |
|---|---|---|
main.log |
Only the main agent (plus framework startup) | You want the primary agent's story without sub-agent noise |
all.log |
Everything, interleaved in true time order: main agent and every sub-agent | You're debugging anything that crosses agents, or just want the full picture. Start here |
sub_<type>_<id>.log |
One file per sub-agent spawned during the run (e.g. sub_research_agent_2a707e74.log) |
A specific delegated job misbehaved; see Sub-agents |
The split works through an attribution tag: every line carries an agent field: main for the main agent, sub:<type>:<id> for lines emitted inside a sub-agent's run (including its actions and LLM calls). main.log and the per-sub-agent files are filtered views of the same stream. all.log keeps the cross-agent ordering that the filtered files lose.
Reading a line¶
2026-07-17 02:17:32.811 | INFO | main | app.scheduler.manager:initialize:83 - [SCHEDULER] Initialized with 5 schedule(s)
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
timestamp level agent module:function:line message
- Level:
DEBUG<INFO<WARNING<ERROR. The file threshold is INFO, and the harness narrates generously at INFO, so most context is captured by default. - Agent:
mainorsub:<type>:<id>(the per-sub-agent files omit this column because the filename already says it). - module:function:line points at the exact source location. Open the module and jump to the line for full context.
- Errors include full tracebacks (
backtraceanddiagnoseare enabled).
Note that the loguru sinks are file-only. The console is not a log sink, so tail the files rather than watching the terminal.
Subsystem tags¶
Most subsystems prefix their messages with a bracketed tag, which makes grep the natural interface:
| Tag | Covers |
|---|---|
[REACT] |
The agent loop: each trigger consumed, each reaction; [REACT ERROR] for caught loop-level exceptions |
[ACTION] |
Action preparation and execution |
[TASK] |
Task lifecycle: create, update, end |
[MEMORY] |
Memory indexing, processing, retrieval. See Memory |
[MCP] |
MCP server init, connection, tool calls |
[SCHEDULER] |
Schedule loops: sleep-until times, wakes, fires. See Scheduling |
[PROACTIVE] |
Proactive heartbeat and planners. See Proactive mode |
[LIMIT] |
Action/token budget warnings and the continue/abort gate |
Grep recipes¶
Find the newest run first, since it's the one you almost always want:
Why did a task fail? Errors first, then rewind for the story leading up to them:
grep -n "ERROR" logs/<run>/all.log | tail -20
grep -n "\[REACT ERROR\]\|\[TASK\]" logs/<run>/all.log
Then open all.log at the line numbers you found and read upward. The [ACTION] and [REACT] lines just before an error usually name the exact action and input that broke.
Follow one action end to end. Every action is logged by name at preparation and execution:
grep -n "web_fetch" logs/<run>/all.log # one action's full trail
grep -n "\[ACTION\]" logs/<run>/all.log | tail # recent action activity
Watch the scheduler live. This shows whether a schedule fired and when it fires next:
You'll see each loop's sleeping until <time> line and Fired schedule: <id> on every fire. This is the fastest way to confirm a schedule is armed and firing.
A sub-agent went wrong. Read its dedicated file (sub_<type>_<id>.log) for the clean story, then find the same timestamps in all.log to see what the main agent was doing around it.
Other log surfaces¶
Two complementary surfaces cover what the run logs don't:
agent_file_system/EVENT.mdis the agent's perspective: the events it produced and observed (actions started/ended, messages, errors), rather than harness internals. It is good for "what did the agent think happened", while the run logs are "what actually happened". See Agent file system.diagnostic/logs/actions/holds per-action JSON dumps (<timestamp>_<action>.log.json) with the full input and output of individual actions, written when actions are exercised through the diagnostic harness (diagnostic/action_diagnose.py). Use it to replay exactly what one action received and returned.
When CraftBot runs as a background service, the service manager keeps its own separate craftbot.log for startup/lifecycle issues ("why didn't it start" rather than "why did it misbehave"). This is covered in Service mode.
Configuration and limits¶
- Location:
logs/in the project root, one folder per process start. Restarting CraftBot begins a fresh folder. If a problem happened "yesterday", it's in an older folder, not the current one. - Rotation:
main.logandall.logrotate at 50 MB and are retained for 14 days. Both are set inapp/logger.py(define_log_level()), which is also where you'd change the format or thresholds. - Sub-agent files exist only for runs that actually spawned sub-agents, and each sink is attached when the sub-agent starts and detached when it ends.
- Size: the INFO threshold is verbose by design. A busy day of tasks produces logs in the hundreds of KB to MB range. Retention keeps this bounded.
Next¶
- Agent file system:
EVENT.mdand the other on-disk records the agent keeps - Scheduling: what the
[SCHEDULER]lines you just grepped actually do - Service mode: the service's own
craftbot.logand where it lives