Claude Codeのworkflow.run_idでWorkflow実行の全イベントを束ねる
OTelのworkflow.run_id(wf_接頭辞)で1回のWorkflow実行のAPI・ツールイベントを復元します。workflow.nameがcustomに伏せられる条件と、parent_agent_idで親を辿る方法もまとめます。
Workflowを何度も走らせていると、「このAPI呼び出しとツール実行は、どの実行のものか」が分からなくなります。workflow.run_idは、その問いに答えるためのOTel属性です。値はwf_で始まる実行ごとの識別子で、これで絞り込むと1回のWorkflow実行が起こしたAPIリクエストとツール結果だけが取り出せます。
属性が加わったのはv2.1.202です。prompt.idはユーザーのプロンプト単位、workflow.run_idはWorkflow実行単位、と粒度が違います。
workflow.run_idが付くイベントと付かないもの
Claude Codeのモニタリング資料では、workflow.run_idは「イベントにだけ付き、メトリクスには付かない属性」に分類されています。値が実行ごとに変わるため、メトリクスに載せると系列が際限なく増えるからです。prompt.idも同じ扱いです。
付く先は、Workflowツールの実行に属するエージェントが出したAPIイベントとツールイベントです。範囲は次のとおり説明されています。
- Workflowスクリプトが起動したエージェント
- そのエージェントがさらに起動したエージェント(スキルの呼び出しなど)
つまり、スクリプトの直下だけでなく、孫の世代までが同じwf_の値を持ちます。値はWorkflowツールの結果が報告する実行IDと一致するので、画面や結果で見た実行を、テレメトリ側の検索キーとしてそのまま使えます。
トレースのスパンにも同じ属性が定義されています。claude_code.llm_requestとclaude_code.toolのスパンの表に、workflow.run_idとworkflow.nameの行があります。トレースは別途ベータ機能の有効化が必要です。
1回の実行を復元する
収集先では、workflow.run_idの値で絞るだけです。次のクエリは、ログを表形式で扱える基盤を想定した例です。テーブル名や列名は収集基盤ごとに変わるため、読み替えが要ります。
-- 例: 1回のWorkflow実行が起こしたイベントを時系列で取り出す
SELECT event_name, agent_id, parent_agent_id, tool_name, model
FROM claude_code_events
WHERE workflow_run_id = 'wf_...'
ORDER BY event_timestamp, event_sequence;並べ替えにはevent.timestampを使い、同じ時刻のイベントだけevent.sequenceで順序を付けます。event.sequenceはプロセス単位のカウンターで、セッションを再開すると値が前後することがあるためです。
この絞り込みで、次のような疑問に答えられます。
- 1回の実行で、APIリクエストは何回発行されたか
- どのツールが失敗し、
tool_resultのsuccessがfalseになったか - どのモデルが何件のリクエストを処理したか
トークンや費用の合計は、イベントではなくカウンターで見る設計です。サブエージェント関連のイベントの説明にも、トークンと費用の集計にはトークンカウンターとコストカウンターを使うよう書かれています。実行単位のコスト把握の考え方は、/workflowsビューの解説の「規模とコストを抑える」と併せて読むと整理しやすくなります。
workflow.nameが伏せられる条件
workflow.nameはworkflow.run_idと一緒に出る属性で、値はスクリプトのmeta.nameです。ここに条件があります。
| 実行されたスクリプト | workflow.nameの値 |
|---|---|
| 同梱の組み込みスクリプトを無改変で実行 | workflow.nameの値名前がそのまま出る |
| ユーザーが作ったスクリプト | workflow.nameの値custom |
| 組み込みスクリプトを編集したコピー | workflow.nameの値custom |
OTEL_LOG_TOOL_DETAILS=1を設定すると、ユーザー作成の名前も実名で出ます。この変数は名前のためだけのスイッチではありません。Bashコマンド、MCPサーバー名とツール名、スキル名、ツール入力まで、ツール詳細をまとめて載せます。実名が要るかどうかだけで有効化せず、組織のテレメトリ方針と合わせて決める必要があります。変数の範囲はOTEL_LOG_TOOL_DETAILSの解説にまとめました。
実務では、名前がcustomのままでも困る場面は多くありません。workflow.run_idは実名と無関係に付くので、実行の束ね方は変わらないからです。困るのは「どのスクリプトが重かったか」を名前で集計したいときだけです。その場合は、名前で集計するのではなく、実行IDとスクリプトの対応を自分たちの側で記録する手もあります。なお、customへの置き換えの対象に/deep-researchのような組み込みの名前は入りません。
parent_agent_idでサブエージェントの親を辿る
workflow.run_idは「同じ実行に属する」ことは示しますが、誰が誰を起動したかまでは示しません。親子関係は別の2属性で見ます。
agent_id: イベントを出したサブエージェントの識別子。メインセッションのイベントには付かないparent_agent_id: そのサブエージェントを起動した、または最後に再開したエージェントのagent_id。親がメインセッションのときは付かない
agent_idが付くのは、サブエージェントのAPIイベントとツールイベント、assistant_response、そして終了時のsubagent_completedです。subagent_completedには所要時間(duration_ms)とツール呼び出し数(total_tool_uses)が入るので、エージェントごとの重さを見るのに向きます。ただしtotal_tokensは最後のリクエスト1回ぶんで、合計ではありません。
どちらもv2.1.310以降が必要です。workflow.run_idの絞り込みにparent_agent_idを重ねると、実行の中の起動ツリーが組み立てられます。parent_agent_idが空のエージェントがツリーの根、あとはparent_agent_idの値が別のエージェントのagent_idと一致する関係を辿ります。
注意点が2つあります。
- 「最後に再開した」とあるため、一度止まって再開されたエージェントの
parent_agent_idは、元の起動者ではなく再開した側を指すことがあります。再開の挙動は止めた実行の戻り方に書いた/workflowsの操作と関係します - Workflowスクリプトが直接起動したエージェントが
parent_agent_idを持つかどうかは、モニタリング資料に記載がありません。ツリーの根をどこに置くかは、手元の実イベントで確かめてから決めます
OTelのログレコードの属性数を絞るSDK側の上限(OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT)を下げている環境では、tool_decisionイベントからparent_agent_idとagent_idが先に欠けます。ツリーが組めないときはこの上限を疑います。
実行ごとの集計に使う
絞り込みの次の使い道は、workflow_run_idでのグループ化です。実行ごとに「APIリクエストの数」「ツール呼び出しの数」「失敗数」を並べると、重かった実行が一目で分かります。
-- 例: 実行ごとのツール失敗数とエージェント数(列名は収集基盤に合わせて読み替え)
SELECT workflow_run_id,
COUNT(DISTINCT agent_id) AS agents,
COUNT(*) FILTER (WHERE event_name = 'tool_result') AS tool_calls,
COUNT(*) FILTER (WHERE event_name = 'tool_result'
AND success = 'false') AS tool_failures
FROM claude_code_events
WHERE workflow_run_id IS NOT NULL
GROUP BY workflow_run_id
ORDER BY tool_failures DESC;successは文字列の"true"か"false"で出るため、真偽値として比較すると取りこぼします。
エージェント数の見積もりは、workflowsの規模の指針と見比べる材料にもなります。指針でsmallを選んだのに実行のエージェント数が想定を超えていれば、スクリプト側の展開が原因です。
実行IDは1つの依頼に1つとは限らない
ultracodeを有効にしたセッションでは、1つの依頼が複数のWorkflowに分かれることがあります。コードの理解、変更、検証が順に別の実行として走る例が、Workflowの説明に挙がっています。このときwf_の値は実行の数だけ現れます。
依頼単位の合計が欲しいなら、workflow.run_idだけでは足りません。同じ時間帯・同じsession.idの実行をまとめるなど、集計側で束ね直す必要があります。
止めた実行を同じスクリプトで再起動した場合に、新しい実行IDが振られるのか、元の値が引き継がれるのかは、モニタリング資料にもWorkflowの資料にも記載がありません。再開前後を同じ実行として数えるかどうかを決める前に、手元で停止と再起動を1回行い、wf_の値を見比べておくと安全です。
起動ツリーを組む例
親子関係の読み方を、架空の値で示します。値は説明用で、実際のIDの形とは異なります。
| agent_id | parent_agent_id | 読み方 |
|---|---|---|
| a1 | parent_agent_id(空) | 読み方ツリーの根。親はメインセッション |
| a2 | parent_agent_ida1 | 読み方a1が起動したエージェント |
| a3 | parent_agent_ida1 | 読み方a1が起動した、a2と同じ階層のエージェント |
| a4 | parent_agent_ida3 | 読み方a3がスキル呼び出しなどで起動した孫 |
同じwf_の中に、この形の行が並びます。再帰的な検索(SQLなら再帰CTE)でparent_agent_idを辿れば、起動の深さごとにAPIやツールの件数を数えられます。孫の世代まで同じworkflow.run_idを持つので、深さで切っても実行単位の合計とは食い違いません。
誰が走らせた実行かも同じ行に載る
イベントには、実行の識別子のほかに標準属性も付きます。user.emailは取得できる場合に常に含まれ、user.account_uuidとorganization.idも取れます。workflow.run_idで束ねた行をそのまま見ると、実行を走らせた人まで特定できます。
チームで共有するダッシュボードに実行単位の表を載せるときは、この点を先に決めておきます。個人を出したくなければ、収集側でuser.emailを落とすか、集計で潰す運用になります。セキュリティ監査の文脈で実行を扱う場合は、OTELイベントと監査項目の対応表の読み方が参考になります。
設定して出力を確かめる
ログのエクスポーターが有効なら、追加の設定は要りません。確認用に標準出力へ出す最小構成は次のとおりです。
export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_LOGS_EXPORTER=console
claudeWorkflowを1回走らせたあと、出力の中でworkflow.run_idを探します。wf_で始まる値が見つかれば成功です。見つからないときの切り分けは次の順です。
CLAUDE_CODE_ENABLE_TELEMETRYが1か。リポジトリの.claude/settings.jsonに書いたOTel変数は無視されるので、シェルか~/.claude/settings.json、管理設定に置くOTEL_LOGS_EXPORTERがnoneでないか。リポジトリ側からnoneにして信号を切ることはできる- Claude Codeがv2.1.202以降か
- 見ているのはメインセッションのイベントではないか。メインセッション自身のイベントには
workflow.run_idが付きません
本番ではconsoleではなくotlpにし、収集先のエンドポイントを指定します。
v2.1.202より古いクライアントが混ざる環境では、その端末の実行にはこの属性が付きません。実行単位の集計から漏れて見えるので、app.versionを有効にしてバージョン別の件数も並べておくと、欠けた理由を切り分けられます。app.versionは既定では出ない属性で、OTEL_METRICS_INCLUDE_VERSIONで有効にします。
prompt.idとの使い分け
| 知りたいこと | 使う属性 |
|---|---|
| 1回のプロンプトで何が起きたか | 使う属性prompt.id |
| 1回のWorkflow実行で何が起きたか | 使う属性workflow.run_id |
| 実行の中の誰が誰を起動したか | 使う属性agent_idとparent_agent_id |
| 1回のAPI呼び出しに対応するツール結果 | 使う属性request_idとtool_use_id |
Workflowはバックグラウンドで走り、1回のプロンプトから複数の実行が始まることもあります。Workflowのイベントにprompt.idも付くかどうかは、モニタリング資料に記載がありません。プロンプト起点で見る調査と、実行起点で見る調査は、別のキーで行う前提にしておくと混乱しません。
拒否や承認待ちを実行単位で見たいときは、safety_eventの集計と組み合わせる手があります。属性の読み方はsafety_eventの集計で扱っています。Workflowを動かす側の使い方はダイナミックワークフローの解説にあります。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。