本文へスキップ
hdknr blog

Claude Code の JSONL ログを読む

Claude Code のセッションログ(JSONL)の保存先、実測したスキーマ(type 17 種類、user 行の 9 割はツール結果)、0 件で成功するパーサを避ける集計手順、usage によるトークン分析

概要

Claude Code はセッションの全履歴を JSONL で書き出しており、直接読めば /cost や /context では見えないターンごとのトークン推移、ツール呼び出しの傾向、コンパクションの位置まで取れる。ただし解説記事のスキーマ記述は環境によって成り立たず、しかも外れ方の多くがエラーではなく出力 0 件の成功になる。

41 セッション・29,043 行(Claude Code 2.1.222〜2.1.258)を横断集計した実測から、パーサを書く前に確かめることをまとめる。数値は絶対値ではなく確認の手順として読む。

詳細

保存先

~/.claude/projects/<encoded-project-path>/<session-uuid>.jsonl

プロジェクトパスはディレクトリ名にエンコードされ(/Users/you/code/my-app → -Users-you-code-my-app)、1 セッション 1 ファイル・追記専用。

~/.claude/sessions/<id>/transcript.jsonl とする解説もあるが、手元には 1 件も無かった。厄介なのは ~/.claude/sessions/ 自体は実在すること。中身はポート番号を冠した .json と権限 600 の .key で、会話ログではない(.key は開いたり貼ったりしない)。存在しないディレクトリなら即座に気づけるが、実在するのに目的のファイルが無い場所には着地してしまう。

JSONL が規格化しているのは封筒だけ

JSON Lines は標準化団体の規格ではなく、要件は UTF-8(BOM なし)・各行が valid な JSON・行終端 \n の 3 つだけ。MIME も未標準化と仕様自身が書いている。

位置づけMIME行区切り
JSON Linesjsonlines.org のコミュニティ仕様application/jsonl(未登録)\n
NDJSONndjson-spec v1.0.0application/x-ndjson(未登録)\n。空行は無視してよい(MAY)
JSON text sequencesRFC 7464(正式 RFC)application/json-seq(IANA 登録済み)各レコードの前に RS 0x1E。JSONL とは非互換

どの仕様も行の中身は決めていない。Claude Code の行スキーマはアプリケーション定義で、参照すべき共通の権威がそもそも無いので、解説どうしが食い違う。

実測したスキーマ

def is_human_utterance(o):
    if o.get('type') != 'user':
        return False
    msg = o.get('message')
    if not isinstance(msg, dict):
        return False
    c = msg.get('content')
    if isinstance(c, str):
        return bool(c.strip())
    if isinstance(c, list):
        return any(i.get('type') == 'text' for i in c if isinstance(i, dict))
    return False

0 件成功を避ける

最初に走らせるべきは整形スクリプトではなく、type の分布とキー出現率を出す 20 行ほどの集計である。

ツール呼び出しとトークン

関連ページ

ソース記事