DEV Community

Agent Island
Agent Island

Posted on • Originally published at agent-island.dev

Parsing Claude Code and Codex Logs Without Double-Counting a Week

Adding every token number found in a JSONL file produces a convincing but unreliable report.

Claude Code and Codex record usage differently. Claude assistant messages expose separate input, output, cache-creation, and cache-read fields. Codex rollout files split the active model and the per-turn usage across different row types. A local ledger needs to preserve those differences before it can combine anything.

Normalize at one boundary

The Claude reader accepts assistant messages with a usage object. It deduplicates a row only when both message ID and request ID exist, skips synthetic model placeholders, and keeps the four token categories separate.

The Codex reader carries the model from turn_context into later token_count events. It uses last_token_usage, not the cumulative total, because cumulative values can repeat earlier work in forked sessions. Cached input is included inside Codex input, so it is subtracted before the remaining input is priced.

Both readers then emit the same event shape:

provider, timestamp, model,
input, output, cache_creation, cache_read
Enter fullscreen mode Exit fullscreen mode

Everything after that point operates on normalized events rather than provider-specific JSON.

Use calendar buckets for a calendar report

The weekly card covers today and the previous six local calendar days. The per-model slice uses that same boundary.

A rolling 168-hour model slice looks reasonable, but it can include part of an eighth calendar day. The model rows then exceed the daily-bar total. Sharing one midnight-aligned boundary removes that mismatch.

The aggregate keeps two token measures. Wire tokens include cache activity; the narrower measure includes input and output. Estimated API value is calculated separately with dated per-token rates.

Keep the share path local

The report image is rendered on the machine. The app writes it to the clipboard or opens the system share picker only after the user clicks a button. It does not upload the report or transcript.

local logs -> token events -> daily aggregates -> local image
Enter fullscreen mode Exit fullscreen mode

That short path is easier to explain and inspect than an analytics-backed report service.

Tradeoffs

Deleted logs cannot be recovered. Local formats can change. A named model missing from the embedded price table is priced at zero and should produce an unpriced warning. An early Codex row with no model context is a separate case: the parser applies its gpt-5.4 model fallback. A token count also says nothing about code quality.

I build Agent Island, the open-source app whose macOS v1.6.1 implementation this article describes. The useful lesson is independent of the app: normalize provider records once, preserve cache semantics, and align every report component to the same time boundary.

The full engineering article and source context are available on the Agent Island Blog.

Top comments (0)