Skip to content

feat(agent): correlate logs by cycle id across the whole call tree - #85

Merged
atomicdragonranch merged 1 commit into
masterfrom
feat/84-correlated-logs
Jul 9, 2026
Merged

atomicdragonranch merged 1 commit into
masterfrom
feat/84-correlated-logs

Conversation

@atomicdragonranch

Copy link
Copy Markdown
Owner

Closes #84.

Problem

The agent had no correlated logs. Every module (main, monitor, executor, escalation, tools/*) logged through a bare per-module stdlib logger. The only id, the cycle int in main.run_loop, was interpolated into the message string (not a filterable field), process-local (reset to 1 on restart, so two runs' cycle-3 collided), and confined to main.py. A cycle's ~30 log lines across the diagnostic sub-agent, its ~8 tool calls, the report sub-agent, and escalation shared nothing tying them together, so overlapping cycles or concurrent incidents were unreconstructable from the logs.

Fix

New logging_setup module:

  • binds a per-cycle correlation id to a ContextVar (propagates across await within the asyncio task, so no threading through signatures);
  • a logging.Filter injects correlation_id onto every LogRecord (attached to the handler, so third-party loggers get it too);
  • configure_logging() (idempotent) installs the format ... [cid=%(correlation_id)s] ..., used by both entry points (agent main, MCP server);
  • new_correlation_id() uses a uuid suffix so ids are unique across restarts.

MonitorAgent.run_cycle sets the id at the top and resets it in finally. Lines outside any cycle render a - sentinel.

Tests

test_logging_setup.py (7): propagation across module loggers, uniqueness across cycles, sentinel outside a cycle, reset restores previous, idempotent configure, format renders without a missing-attribute crash. test_monitor.py (+2): run_cycle binds a real cyc- id during the cycle and clears it after; two cycles get distinct ids.

Full suite 216 passed; ruff + mypy clean.

Follow-up (not here): JSON handler for prod per mb_observability. This delivers the correlation id, the load-bearing part.

https://claude.ai/code/session_01R5VygSzbGTggW7mHd3PVwE

Closes #84.

The agent had no correlated logs: every module logged through a bare
per-module stdlib logger, and the only id (the `cycle` int in main) was
interpolated into the message string, process-local (collided across
restarts), and never left main.py. A cycle's ~30 log lines across the
diagnostic sub-agent, its tool calls, the report sub-agent, and escalation
shared nothing tying them together, so overlapping cycles were
unreconstructable.

Add a shared logging_setup module that binds a per-cycle correlation id to a
ContextVar (propagates across await within the task) and a logging.Filter
that injects it onto every LogRecord. run_cycle sets a uuid-suffixed id
(unique across restarts) at the top and resets it in finally; both entry
points (agent main, MCP server) now configure logging through the shared
helper so the format carries cid= for every line. Lines outside any cycle
render a "-" sentinel.

Tests: correlation propagation across module loggers, uniqueness across
cycles, sentinel outside a cycle, idempotent configure, and run_cycle
binding/reset end to end. Full suite 216 passed; ruff + mypy clean.
@atomicdragonranch
atomicdragonranch merged commit 4a5842a into master Jul 9, 2026
3 checks passed
@atomicdragonranch
atomicdragonranch deleted the feat/84-correlated-logs branch July 9, 2026 18:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(agent): correlate logs by cycle id across the whole agent call tree

1 participant