Skip to main content
Laravel, thinking fast.

Chapter 14

Observability for Things You Cannot Reproduce

Julian Beaujardin

The bug report says the site it built was wrong.

You have the conversation. You do not have the turn: not which tools ran, not how long any of it took, not what the model was thinking, not what it cost. And you cannot re-run it, because re-running it produces a different turn.

That is the whole difficulty. In ordinary debugging, reproduction is the first step. Here it is unavailable, and what you logged at the time is all you will ever have.

Names and Sizes, Never Inputs

Before deciding what to log, decide what must never be logged.

// Deliberately logs the tool name and nothing else from the payload: the
// arguments carry the business description, the email and the address.

Tool arguments are the richest, most tempting data in the system and the most dangerous. They are exactly what a user typed — their email, their address, their phone number, their description of their business. A log line containing them is personal data in a system with a long retention period, wide read access and no consent trail.

The rule that has held up: names and sizes, never inputs or results.

Log::info('ai_widget.round_trip', [
    'iteration' => $iteration,
    'duration_ms' => $durationMs,
    'stop_reason' => $stopReason,
    'tool_count' => count($tools),
    'tools' => $tools,
    // A proxy for how much of the time was generation rather than transport:
    // a long reply legitimately takes longer.
    'text_chars' => array_sum(array_map(
        fn (array $block): int => mb_strlen((string) ($block['text'] ?? '')),
        $textBlocks
    )),
]);

Which tools ran, how many, how long, why it stopped, and how many characters came back rather than the characters themselves. That last one is the trick worth stealing: text_chars answers "was this a long reply or a slow one?" without storing a word of it.

You will be tempted, during an incident, to add the payloads "just temporarily". Do not. Temporary logging outlives the incident, and the retention clock started when you shipped it.

One Structured Line Per Round Trip

The unit is the round trip, not the turn — a turn is many calls with different shapes, and averaging them hides what you need.

iteration is what makes a turn reconstructable afterwards: four lines with iterations 1–4 tell you the shape of the whole thing. Which tools fired and when. Whether it stopped because it was done, or because a cap bound. Where the time went.

Pair it with one summary line per turn — the token accounting from Chapter 4 — and between them you can answer nearly any question about a conversation you cannot re-run.