Nabil Asofi

Journal / systems

Agent observability: if it runs unattended, it must log

Agent observability isn't optional once no one is watching the screen. What to log, why silent failures are the default, and what counts as enough.

Agent observability is not a nice-to-have for anything that runs without a person watching it. If an agent processes a queue at 3am, retries a failed call, or decides on its own to skip a step, the only record of what happened is whatever you chose to write down. No log means no evidence, and no evidence means you find out something went wrong from a customer, not from your system.

This is the part teams skip when they move from a demo to production. The demo has a terminal window open. Someone is watching the output scroll by, mentally logging the behavior themselves. Production has none of that. The agent runs on a schedule, in a container, on a server nobody is looking at, and the moment you stop looking is the moment you need the system to start talking to you in its absence.

Agent observability starts where the UI ends

A chat interface or a dashboard gives you a false sense of coverage, because you can always just look. Unattended agents have no such fallback. The question to ask before deploying anything headless is: if this fails silently at 2am, what tells me by 9am? If the honest answer is “nothing,” you don’t have an agent workflow, you have a liability with a cron entry.

The fix is not complicated, but it is tedious, which is why it gets skipped. Every run needs a start event, an end event, and a status. Every external call (API, database write, tool invocation) needs its inputs, its outputs, and its latency logged on both success and failure, because you cannot tell a slow degradation from a stable baseline if you only ever look at errors.

Silent failure is the default, not the exception

An agent that fails loudly is the easy case. It throws, the process exits, your monitoring catches the exit code, you get paged. The failure mode that actually hurts you is the one where the agent completes successfully, logs a status of “done,” and did the wrong thing anyway: it summarized the wrong document, it sent the email to the wrong list, it decided a step wasn’t necessary and quietly skipped it. None of that trips an error handler. All of it looks, from the outside, like success.

This is the same problem that shows up in idempotent cron jobs: a missed run and a corrupted run both report back as “fine” unless you specifically instrument for the difference. The lesson generalizes. Logging exit status is necessary and nowhere near sufficient. You need to log the decision, not just the outcome: which branch the agent took, which tool it chose not to call, what input made it choose that path. Without that, debugging a bad result six days later means re-running the whole thing and hoping it fails the same way twice, which it often won’t.

The handoff point is where observability pays off

Every agent workflow has at least one moment where control should pass to a human, and that moment is exactly where logging earns its keep. If a checkpoint exists, you need a record of what the agent was about to do, why it stopped, and what the human actually approved, not just that approval happened. I’ve written before about designing the human handoff in agentic workflows, and the failure mode there is the same one: a checkpoint with no log is a checkpoint nobody can audit later, which means it’s theater dressed as safety.

Logging at the handoff also protects you from the opposite failure: a human approving something without enough context, then the agent executing a decision that was never really reviewed. The log is the only proof, after the fact, that the review was real.

What counts as enough

Enough means you could hand the log to someone who was not there and they could reconstruct what happened without asking you anything. That’s the actual bar, not “we have some logs.” When I built out a real agent workflow for a production queue, the logging rewrite took longer than the agent logic itself, and it was the part that made the system trustworthy enough to leave running overnight.

Structured logs beat prose logs. A line that reads step=validate status=skip reason=already_processed id=4471 is searchable, countable, and alertable. A line that reads “skipping because already done” is not. If you can’t grep it, you can’t monitor it, and if you can’t monitor it, you don’t actually know the agent is doing what you think it’s doing. You’re just hoping.

Also on this subject