Skip to main content

Audit Claude Code JSONL session logs: track cost, cache health, context blow-ups, and 5h-window pressure.

Project description

tokenol

Audit Claude Code JSONL session logs for cost, cache health, context blow-ups, and 5-hour rate-limit pressure.

tokenol parses the session transcripts that Claude Code writes to ~/.claude*/projects/**/*.jsonl and produces per-day, per-session, per-project, and per-model rollups — plus a live burn-rate view for the active 5-hour window.

Why tokenol

Claude Code bills you for everything the model reads — input, output, and cache creation/reads. When the prompt cache is working, 95%+ of your context tokens cost a tenth of full input price. When it isn't — idle gaps past the 5-minute TTL, context compaction, two sessions in the same repo thrashing each other — the same conversation can cost 10× more without looking any different.

tokenol tells you which sessions, projects, and hours did that, and usually why. You run it locally over the JSONL logs Claude Code already writes; nothing is uploaded anywhere.

Dashboard

Main dashboard

Session drill-down — pattern detection + cost-per-turn small multiples:

Session drill-down, top Session drill-down, lower panels

Project page — cache efficiency trend, verdict distribution, top turns:

Project page

Install

pipx install tokenol

Requires Python 3.10+.

Quick start

# Daily token / cost aggregates over the last 14 days
tokenol daily

# Hourly breakdown for today
tokenol hourly

# Top 10 most expensive sessions in the last 30 days
tokenol sessions --since 30d --top 10 --sort cost

# Per-project rollup
tokenol projects

# Live view: burn rate + projected end-of-window cost
tokenol live --last 20m

All commands scan every JSONL file under $CLAUDE_CONFIG_DIR (falling back to the standard ~/.claude* locations) and deduplicate turns using the same message.id:requestId compound key that ccusage uses.

Scanning multiple projects

If you use workspace isolation (one ~/.claude-<project> directory per repo, pointed at via CLAUDE_CONFIG_DIR), tokenol by default only sees the currently-active project. Pass --all-projects (or -A) to any command to scan every ~/.claude* directory and get a cross-project view:

# Total spend across every project in the last 14 days
tokenol daily --since 14d --all-projects

# Which sessions cost the most, globally
tokenol sessions --since 30d --top 10 -A

You can also set CLAUDE_CONFIG_DIR to a colon- or comma-separated list of paths to scan a specific subset.

Commands

Command What it shows
daily Per-day tokens (input, output, cache read/creation), cost, turn count
hourly Per-hour breakdown for a single day (defaults to today)
live Active 5-hour window burn rate, recent-activity rate, projected final cost
sessions Per-session detail table with blow-up verdict (RUNAWAY, CONTEXT_CREEP, …)
projects Per-project rollup grouped by cwd
models Per-model rollup with tool-use counts and error rates
verify Cross-check tokenol totals against ccusage --json (if installed)
serve Launch a local browser dashboard with live burn-rate gauge and all panels

Every command accepts:

  • --since 14d — lookback window (e.g. 7d, 30d, or an ISO date)
  • --all-projects / -A — scan every ~/.claude* directory (ignores CLAUDE_CONFIG_DIR)
  • --strict — exit non-zero if any cost-computation assumption fired
  • --show-assumptions — always print the assumption footer
  • --log-level debug|info|warning

tokenol sessions additionally takes --sort (cost, input, output, cache_read, turns, max_input, duration) and --top.

tokenol live takes --last 20m|2h|30s and exits non-zero if the projected window cost exceeds the configured reference.

Live dashboard

# Install with dashboard dependencies
pipx install 'tokenol[serve]'

# Start the dashboard (binds to http://127.0.0.1:8787)
tokenol serve

# Cross-project view, faster tick, custom reference threshold
tokenol serve --all-projects --tick 2s --reference 25

# Open browser automatically
tokenol serve --open

The dashboard updates via SSE as Claude Code writes events to disk. Main page layout (top to bottom):

Panel What it shows
Topbar Today's cost · sessions · output · last-active time; global period selector (Today / 7D / 30D / All)
Efficiency tiles Hit% · $/kW · Ctx · Cache reuse — each with a delta chip vs 7-day median and colour-coded threshold
Hour By Hour Hourly metric timeline with day-picker, metric pills, project/model filters, and click-to-drilldown
Daily History 30-day metric history with 7-day moving average overlay; range pills (7D / 30D / 90D / All)
Models Per-model cost, turns, output, and efficiency metrics; local range override; click row → /model/<name>
Recent Activity Active projects in the last 60 min with Ctx used, $/kW, hit%, verdict; sortable; click row → /project/<cwd>

Keyboard shortcuts: ? Glossary · / Find · , Settings · Esc close/back · g t scroll to top · ↑↓ Enter table row navigation · ← → chart cursor.

Efficiency metric glossary

Metric Definition Target
Hit% cache_read / (cache_read + cache_creation + input) ≥ 95%
$/kW cost × 1000 / output_tokens — dollars per 1k output tokens < $0.20
Ctx cache_read / output as N:1 — context tokens read per output token < 400:1
Cache reuse cache_read / cache_creation as N:1 — low = cache thrashing > 50:1
Ctx used Latest turn's visible context ÷ model context window < 85%

Preferences

User preferences (tick cadence and threshold overrides) are saved to:

$XDG_CONFIG_HOME/tokenol/prefs.json   # default: ~/.config/tokenol/prefs.json

Shape:

{
  "tick_seconds": 300,
  "reference_usd": 50.0,
  "thresholds": {
    "hit_rate_good_pct": 95,
    "hit_rate_red_pct": 85,
    "cost_per_kw_good": 0.20,
    "cost_per_kw_red": 0.40,
    "ctx_ratio_red": 400.0,
    "cache_reuse_good": 50.0,
    "cache_reuse_red": 20.0
  }
}

Reset to defaults via the Settings modal (POST /api/prefs {"thresholds": "reset"}).

Session drill-down

Click any session to open the drill-down page (/session/<id>). It shows:

  • What likely went wrong — automated pattern cards at the top of the page, each with a headline, the measurable signal that triggered it, and a suggested fix. Five patterns are detected:

    Pattern Signal
    Idle expiry Gap ≥ 1 h between turns + next turn was ≥ 80% cache_creation — the 5-minute prompt-cache TTL expired
    Compaction re-inflation Visible-token count dropped then climbed back to ≥ 80% of the previous peak — compacting but immediately refilling the context
    Context ceiling plateau ≥ 20 consecutive turns at ≥ 90% of the model's context window — paying near-full-context input rates throughout
    Sidechain explosion Sidechain/task-agent work accounts for > 40% of session cost
    Tool error storm > 20% error rate across any 10-turn window
  • Cost per turn — stacked bar chart (input / output / cache_read / cache_creation). Toggle "All" or "Top 30" to focus on the most expensive turns. Click any bar to open the per-turn detail modal.

  • Per-turn modal — cost component breakdown, token counts, tool call results (✓/✗), first 500 chars of the user prompt and assistant preview. Navigate with ← / → or close with Esc.

What it detects

For every session, tokenol computes a blow-up verdict against spec-defined thresholds:

Verdict (table label) Trigger
RUNAWAY_WINDOW (runaway) Any 5-hour window costs ≥ $50
CONTEXT_CREEP (ctx-creep) Max single-turn input ≥ 500k and growth ≥ 2k/turn
TOOL_ERROR_STORM (tool-errs) ≥ 10 tool uses with > 30% error rate
SIDECHAIN_HEAVY (sidechain) Sidechain session costing > $5
OK (ok) Everything else

Daily efficiency columns

The tokenol daily report shows these cost/cache efficiency ratios:

Column Meaning Target
$/kW USD per 1,000 output tokens < $0.20
Ctx Context tokens read per output token (N:1) lower is better
Cache reuse Cache reads per cache-creation token (N:1) > 50:1
Hit% % of context served from prompt cache ≥ 95%

Pricing

Flat per-model rates (no 1M-token tier surcharge — matches ccusage's default behaviour). The current registry lives in src/tokenol/metrics/cost.py. When a turn's model isn't in the registry, tokenol records an UNKNOWN_MODEL_FALLBACK assumption tag and uses a conservative default; run with --show-assumptions or --strict to surface these.

See docs/METRICS.md for metric definitions and docs/ASSUMPTIONS.md for the full list of assumption tags.

Development

git clone https://github.com/farhanferoz/tokenol
cd tokenol
uv sync --extra dev
uv run pytest
uv run ruff check

Licence

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tokenol-0.1.0.tar.gz (1.4 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tokenol-0.1.0-py3-none-any.whl (110.3 kB view details)

Uploaded Python 3

File details

Details for the file tokenol-0.1.0.tar.gz.

File metadata

  • Download URL: tokenol-0.1.0.tar.gz
  • Upload date:
  • Size: 1.4 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for tokenol-0.1.0.tar.gz
Algorithm Hash digest
SHA256 fe380c007c7a5557610c7a2b569d5e172aa01a31170d12b2cf86eb15d77f58b6
MD5 9217af5c74c64eeffaf2c7f075c36cde
BLAKE2b-256 cff2b9c7e7ae3abf4293a32ab79bdf688454a0b790fa5e1d5588353194838363

See more details on using hashes here.

File details

Details for the file tokenol-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: tokenol-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 110.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for tokenol-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5a53e2950566ebac5a9c37cb52565cddabe8eaf3ad8c69e700d3337604079d09
MD5 a587a47eee05904fe8b7f1b35adf1fc7
BLAKE2b-256 6c8252093d22ac0cc71ae98b96d58b55da21b37f451a81ff8ce04f129d9bcdd5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page