AI Agent Logging: Understand Actions Without Leaking Secrets
Design AI agent logs and run receipts that explain what ran, what changed and why it stopped, while keeping secrets and personal data out. Schema, redaction and retention.
Contents
- Decide what questions your logs must answer
- Define a small event and receipt schema
- Connect approvals to actual execution
- Redact before sensitive data reaches the log
- Diagnose one failed run end to end
- Use logs for operations without overcollecting
- Frequently asked questions
- Do I need access to the model's hidden reasoning?
- Should I store every prompt and tool response?
- How do I trace a retried action without confusing it with a new one?
- Can logs leak personal information?
Image: Aaron Burden on Unsplash.
A good agent log answers five questions about every run: what was asked, what was read, what was done, what was approved, and why it stopped. It should do that without becoming a second copy of everything sensitive the assistant ever touched.
Logs fail in two opposite ways.
Too little, and a failure is a mystery.
Too much, and the log is the leak.
This guide covers personal-scale logging and run receipts. For tracing standards such as OpenTelemetry's generative AI conventions, see AI agent observability. Both are part of the AI harness engineering guide.
Decide what questions your logs must answer
Start from the questions you will ask when something goes wrong, not from what is easy to dump.
| Question | Needed in the log |
|---|---|
| What ran? | Run ID, task, start and end time, harness and model versions |
| What did it read? | Source IDs and document references, not full content |
| What did it do? | Each tool call, its arguments (redacted), result status |
| What changed? | Final-state checks, external effects with their IDs |
| Who approved what? | Approval IDs linked to the actions they authorised |
| Why did it stop? | Stop reason: answered, timeout, step limit, budget, error, cancelled |
| What did it cost? | Tokens, tool calls, retries, duration |
If a field does not help answer one of these, question whether you need it.
Define a small event and receipt schema
Two layers work well: events as the run happens, and a receipt summarising it.
An event:
{
"run_id": "run_2026-10-18_0812_a41",
"attempt": 1,
"seq": 3,
"type": "tool_call",
"tool": "calendar.list_events",
"args": { "calendar": "work", "days": 1 },
"status": "ok",
"duration_ms": 412,
"at": "2026-10-18T08:12:07Z"
}A receipt:
{
"run_id": "run_2026-10-18_0812_a41",
"task": "Prepare today's meeting brief",
"versions": { "harness": "0.4.2", "model": "provider/model-name@2026-09" },
"sources": ["note:alpha-plan", "note:sam-1on1", "event:cal_88f2"],
"actions": [{ "tool": "notes.save_draft", "status": "ok", "effect_id": "draft_19c" }],
"approvals": [],
"data_destinations": ["model provider (cloud)", "calendar API (read)"],
"stop": "answered",
"usage": { "input_tokens": 9120, "output_tokens": 640, "tool_calls": 3, "retries": 0 },
"duration_ms": 18400
}Note what is absent: the full note text, the full brief, credentials.
Record observable decisions, such as which tool was called with which arguments and a short stated reason if the model provides one. Do not rely on, or promise, access to a model's hidden reasoning.
Connect approvals to actual execution
For any consequential action, the log must link three things:
- The approval: who approved which exact action, and when.
- The attempt: the tool call that tried to perform it.
- The effect: the downstream ID proving it happened, such as a message ID.
If a call times out, log the outcome as uncertain, not failed. Then log the reconciliation step that resolved it. That chain is what lets you prove an email was sent once, not twice. The permissions guide and scheduling guide cover the approval and retry sides.
Redact before sensitive data reaches the log
Redaction applied later is redaction that sometimes does not happen. Apply it where events are written.
Allowlist fields rather than blocklisting secrets. Log tool, status and selected argument names by default; add content only where a specific question needs it.
Never log:
API keys, tokens, passwords and cookies,
full message bodies and document contents by default,
payment and identity numbers.
Scan errors too. Exception messages and provider error bodies often echo the request, including content and headers.
Test redaction. Put synthetic secrets, such as CANARY-KEY-4417, into tool arguments and error paths during testing, then search the logs for them. Finding one is a failure.
Fail closed. If the redaction step errors, drop the field rather than writing it raw.
Diagnose one failed run end to end
A user reports: "The brief missed the Alpha review."
- Find the run by date and task.
- Check the receipt sources. Was the Alpha plan retrieved? If not, the problem is retrieval or context, not writing.
- Check the tool events. Did
calendar.list_eventsreturn the review? A status ofokwith zero events points at the query or scope. - Check versions. Did the model or harness change since the last good run?
- Reproduce with the same inputs and versions.
- Fix the responsible mechanism and add a regression case.
Without run IDs, sources and tool statuses, step 2 is guesswork. With them, it takes minutes. The troubleshooting guide continues from here.
Use logs for operations without overcollecting
Turn logs into a few useful signals:
| Signal | Why it matters |
|---|---|
Runs ending in step_limit or timeout |
Loops and stuck tasks |
| Tool error rate by tool | One flaky connector |
uncertain outcomes |
Possible duplicate actions to reconcile |
| Spend per day | Budget drift |
| Evaluation failures after a change | Regressions |
Set retention deliberately. Keep receipts long enough to investigate problems and compare costs; expire detailed events sooner. Restrict who and what can read logs. Include logs in your backup plan, and remember that deleting a memory does not delete old log lines that mentioned it. The memory guide covers that limit.
Frequently asked questions
Do I need access to the model's hidden reasoning?
No. Log the observable decisions: inputs, tool calls, results, approvals and stop reasons. Those are enough to debug most failures.
Should I store every prompt and tool response?
Not by default. Store references and statuses, and capture full content only for specific debugging, with short retention.
How do I trace a retried action without confusing it with a new one?
Give each run an ID, each retry an attempt number, and each external effect its downstream ID.
Can logs leak personal information?
Yes. Treat logs as sensitive data: redact at write time, restrict access and expire them.