claude/agentic-engineering-principles-plugin/skills/logging-for-agent-debugging/SKILL.md
Design structured logging that AI agents can actually debug from — a stable documented event vocabulary, ambient trace context, scoped routing with a global timeline, documented query gotchas, a bounded log-analysis CLI, and forensic per-run logs for long agent executions. Use when designing logging for a project agents will debug, when an agent cannot find what it needs in the logs, when building an agent-friendly log query or performance-analysis tool, or when adding observability to multi-step agent runs.
npx skillsauth add amhuppert/my-ai-resources logging-for-agent-debuggingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Human-oriented logging assumes a person who already knows the system scrolls a terminal. Agent-oriented logging assumes a caller who must find the relevant twenty lines out of two million by grepping for names it was told exist. That single change of consumer drives everything below: event names become an API, every record must self-join, and the query traps must be documented as loudly as the schema.
Treat log event names as a public API with the same stability obligations as any other interface.
module.event — prompt.submit, request.complete, lock.rejected, session.create_failure. Predictable prefixes mean an agent can grep a whole subsystem ("message":"prompt.) without knowing every leaf name.grep | jq works on any subset without repairing structure. Multi-line pretty-printed logs are unqueryable by the tools agents reach for first.Example record:
{"timestamp":"2026-01-14T09:31:07.412Z","level":"info","module":"tracing","message":"request.complete","traceId":"1f4c…","action":"send-prompt","projectName":"acme","sessionName":"fix-auth","status":200,"durationMs":842}
Example catalog rows:
| Module | Event | Level | Key fields | Meaning |
|---|---|---|---|---|
| tracing | request.complete | info | method, path, status, durationMs | Request finished (non-streaming) |
| sessions | session.create | info | projectName, sessionName, mode | Unit of work created successfully |
| lock | lock.rejected | warn | scope, holderTraceId | Concurrent operation blocked by single-flight lock |
One concept, one field name, everywhere. If duration is durationMs in one module, totalMs in another, and elapsed in a third, then no single query answers "what was slow" — and the agent that writes the obvious query gets a confidently wrong empty result.
durationMs, traceId, error, status) and enforce them.durationMs plus waitMs/holdMs where durationMs = waitMs + holdMs.totalMs; the analysis parser still reads it as a fallback"). Otherwise old records look like missing data.Every record should carry enough identity that "everything that happened during that one action" collapses to a single grep — not a manual join across five files.
traceId plus domain identity (project / session / conversation, or your domain's equivalents).runAsTrace(action, fn)) and require it.Drill-downs should be small; timelines should be complete. You need both.
request.start / request.complete / request.error) to the global log as well, so a chronological cross-cutting timeline always exists. Without this, scoped routing destroys the ability to see ordering across entities.[A-Za-z0-9._-] to _, strip leading dots, truncate long names with a hash suffix.logger.path.sanitize_failure). A lost line is a lie by omission.A logging doc that describes only the schema teaches agents to write queries that read clean and are wrong. Document the ways a query silently lies.
Rotation is the canonical correctness trap. With size-based rotation, the active file holds only the newest window; older records live in numbered backups. So:
tail and "most recent" checks are correct against the active file..1 backup, not gone.Use grep -h across the glob so the output has no filename: prefixes and stays valid NDJSON for jq (line ordering is irrelevant when each record is timestamped):
grep -h '"level":"error"' "$LOG_DIR"/app.log* | jq -r .message | sort | uniq -c | sort -rn
grep -h 'TRACE_ID' "$LOG_DIR"/app.log* | jq '{timestamp,module,message,durationMs}'
Also document: which files are auto-discovered by tooling and which must be passed explicitly; that debug-level events (locks, verbose internals) are absent unless the level is raised; and any event emitted only above a threshold, since its absence means "fast", not "never happened".
Teach an orientation-first workflow. Before any deep dive:
wc -l) — catch a just-rotated or wrong-path file immediately.traceId.Agents that skip orientation anchor on the first error they see rather than the dominant one.
Reading a large log costs tokens proportional to its size and returns evidence in no particular order. Give agents a command that returns bounded structured output so they rank evidence instead of reading it.
Capabilities worth building, roughly in order of value:
Two properties matter more than the feature list:
Support performance budgets as checked-in data evaluated by the same tool: run advisory first to calibrate against real numbers, then gate. A budget file in the repo is reviewable and diffable; a threshold buried in a script is not.
For multi-step agent executions (workflows, orchestrated runs, multi-agent pipelines), general application logs are the wrong shape. Write a dedicated per-execution log tree, split by concern.
runs/<executionId>/
├── _manifest.json # Entry point: status, halt reason, definition, summaries
├── lifecycle.jsonl # started / paused / resumed / aborted / completed / halted
├── decisions.jsonl # cross-cutting decisions: scheduling, rotation, retry, circuit breaker
└── units/<unitId>/
├── iterations.jsonl # per-iteration lifecycle
├── tasks.jsonl # task completion, reopening, agent-added tasks
├── validation.jsonl # validator invocations, results, remediation
└── prompts/ # full prompts and responses, verbatim
Design rules:
{ timestamp, event, executionId, ...data }) so one query pattern works everywhere.await a forensic write on the critical path, never let it throw into business logic.argsPreview, promptLength) instead of payloads.conversationId is safe; the conversation is not.| Symptom | Cause | Fix |
|---|---|---|
| "The agent can't find anything in the logs" | Events exist in code but not in a catalog the agent was given | Publish the catalog table; treat uncatalogued events as undiscoverable |
| A duration query returns nothing meaningful | Field names diverged (totalMs vs elapsed vs durationMs) | Canonicalize; document legacy fallbacks |
| Error counts look implausibly low | Query hit only the active file after rotation | Glob rotated backups with grep -h |
| Background work has no logs | Jobs and async turns never entered the trace mechanism | Require an explicit trace entrypoint; assert coverage in a test |
| Trace shows mostly unexplained time | Instrumentation gap, not a code problem | Add timing coverage before optimizing |
| A scoped log is missing a whole action | Routing failed and dropped, or the entity was never in context | Degrade to the next scope with a diagnostic event |
| Agent read the whole log and ran out of context | No bounded analysis command existed | Provide ranked, capped structured output as the first resort |
| An agent run cannot be reconstructed after the fact | Prompts were summarized rather than stored | Capture verbatim in the forensic store |
ai-readable-tool-output — configure linters, compilers, and test runners for low-noise agent consumptionlive-system-verification — verify features against the running system and durable state, not fakes or UIagent-retrospectives — reflect on agent instructions, skills, process, and tooling after real runsdevelopment
Debug a running web app via the web-debugger SDK: app logs, application state, runtime snapshots, React state, query cache.
development
Thoroughly understand a software development objective before implementation: research, identify ambiguities, ask clarifying questions. Use before starting implementation of a non-trivial or ambiguously specified feature, or when requirements leave open design decisions.
development
Locate the on-disk Claude Code transcript file (.jsonl under ~/.claude/projects/) for the current or a specified conversation.
development
Reflect on codebase navigation effectiveness at end of conversation. Surfaces dead ends, inefficiencies, missing context. Does not write files — pair with /kiro:steering-custom to persist.