exploring-apm-traces について
exploring-apm-tracesは、PostHogが収集したOpenTelemetryの分散トレースを調査するスキルです。専用のAPMツールでサービスの健全性を評価し、レイテンシやエラーを特定してトレースとスパンのデータを確認できます。
主な機能
- 01ベースラインとのサービス健全性比較
- 02スパン検索とトレース全体の取得
- 03レイテンシ、エラー、操作の集計
- 04コールツリーとスパン属性の確認
- 05スクリプトによる大規模トレース結果の解析
活用シーン
- 01健全性が悪化したサービスの特定
- 02遅いリクエストの呼び出し経路の追跡
- 03トレース内でエラーが発生した場所の特定
- 04異常なスパン間の属性比較
MCP 対応
このスキルはPostHog MCPツールでAPMスパンを検索し、トレースを取得してサービスやスパンのデータを集計します。SLOバーンの質問では、APM分析の前にガバナンス対象のメトリクスツールを使うよう案内します。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ posthog/ skills --skill exploring-apm-tracesインストール後、Claude Code で/exploring-apm-tracesを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/spans-and-fields.mdscripts/_common.pyextract_span.pyprint_summary.pyprint_timeline.pysearch_spans.pyshow_structure.py詳細
APM トレースの調査(OpenTelemetry スパン)
PostHog は OpenTelemetry から分散トレースを取得します。各トレースは、リクエストがサービス間を通過する経路を表すスパンのツリーです。
区別について: このスキルは APM / OpenTelemetry トレース向けです。AI オブザーバビリティのトレース(エージェント/モデルの $ai_* イベント)やログ(posthog:query-logs、posthog:logs-*)と混同しないでください。
ガバナンス対象メトリクスを最初に確認
質問が SLO のバーンについて尋ねている場合は、APM ツールを使う前に posthog:metric-list を呼び出し、slo_explicit_burn_by_operation を探してください。承認済みでドリフトのない該当メトリクスを posthog:data-catalog-metric-run で実行し、正式なヘッドラインとして使用します。ユーザーがバーンの原因となるオペレーションも尋ねている場合は、まずヘッドラインに回答してから、以下の集計およびトレースのワークフローを使って正式な指標ではない内訳を示します。該当するガバナンス対象メトリクスがない場合は、その旨を伝え、算出した指標が正式な指標ではないことを明記してください。
利用可能なツール
| ツール | 用途 |
|---|---|
posthog:apm-service-health | ベースラインと照らしたサービスごとのステータス:ここから開始します |
posthog:query-apm-spans | スパンの検索と絞り込み(コンパクトなリスト表示) |
posthog:apm-trace-get | 1 つの 16 進数 trace_id に含まれる全スパンの取得 |
posthog:apm-spans-aggregate | オペレーションごとの集計(件数、p50/p95、エラー) |
posthog:apm-spans-tree | (parent, child) エッジごとのコールツリー集計 |
posthog:apm-spans-count | スカラーのスパン件数 — 低コストのフィルター事前チェック |
posthog:apm-spans-sparkline | 時系列のスパン件数(ゼロ埋め時系列) |
posthog:apm-spans-duration-histogram | 対数スケールの期間バケットごとのトレース件数 |
posthog:apm-attribute-breakdown | 1 つの属性値でグループ化したスパン件数 |
posthog:apm-services-list | 重複のないサービス名の一覧 |
posthog:apm-attributes-list | スパンまたはリソース属性キーの一覧 |
posthog:apm-attribute-values-list | 特定の属性キーに対する値の一覧 |
レスポンススキーマと kind/status_code の列挙値については、references/spans-and-fields.md を参照してください。
ワークフロー:URL からトレースをデバッグする
ステップ 1 — トレースを取得する
posthog:apm-trace-get
{
"trace_id": "<hex_trace_id>"
}
レスポンスは { results: [span, span, …], _posthogUrl: "…" } です。トレース内のすべてのスパンを含むフラットなリストです。
ファンアウトするリクエストフローではリストが非常に大きくなる場合があります。インラインの上限を超えると、Claude Code が自動的にファイルへ保存します。
結果から、次の情報を取得できます。
name、service_name、kind、status_code、parent_span_id、duration_nano、is_root_spanを含むすべてのスパン- トレース UI でこのトレースを開くディープリンク
_posthogUrl。ユーザーがクリックして確認できるよう、必ず回答に含めてください
ステップ 2 — スクリプトで大きな結果を解析する
結果がファイルに保存された場合(複数のサービスにまたがる数百個のスパンを含むトレースなど)は、解析スクリプトを使って調査してください。
全体像を把握するため、まずサマリーを確認してから、詳細を掘り下げます。
# 1. Overview: services, span count, slowest spans, errors
python3 scripts/print_summary.py /path/to/persisted-file.json
# 2. Indented chronological tree (DFS by parent_span_id)
python3 scripts/print_timeline.py /path/to/persisted-file.json
# 3. Drill into a specific span by name
SPAN="HTTP GET /api/users" python3 scripts/extract_span.py /path/to/persisted-file.json
# 4. Search for a keyword across span names, services, IDs
SEARCH="keyword" python3 scripts/search_spans.py /path/to/persisted-file.json
# 5. When the JSON shape looks unfamiliar
python3 scripts/show_structure.py /path/to/persisted-file.json
すべてのスクリプトで、切り詰める長さを指定する MAX_LEN=N 環境変数を利用できます(0 = 無制限)。
ツリーの再構築(parent_span_id → span_id)
フラットなスパンの一覧はツリー構造です。各スパンには、次の情報が含まれています。
trace_id— トレース内のすべてのスパンで共通span_id— このスパン固有の 16 進数 IDparent_span_id— 親のspan_idを指します(ルートの場合はゼロ埋めされた 16 進数の000…000)is_root_span— トレースのエントリを示す便宜的なフラグ
ツリーを再構築する手順は次のとおりです。
is_root_spanが true のスパン(またはparent_span_id == "00000000…"のスパン)がルートスパンです。- それ以外の各スパンは、
span_idがそのparent_span_idと一致するスパンの子です。 parent_span_idでグループ化し、各ルートから下方向にたどります。
scripts/print_timeline.py がこの処理を行い、深さ優先探索のインデント付きツリーを出力します。
調査パターン
「サービスの健全性はどうですか?」(まずはこちら)
apm-service-healthは、1 回の呼び出しですべてのサービスを評価します。リクエスト数、エラー率、p50/p95/p99、ベースライン期間からの変化、status(silent、regressed、degraded、new、recovered、improved、ok)とそのreasonを確認できます。行は問題が深刻な順に表示されます。compareでベースラインを選択します。previous_period(デフォルト)、previous_day、previous_week、またはqueryと併せて指定する明示的なbaselineDateRangeを選べます。environmentとserviceNamesで対象を絞り込みます。nextにある呼び出しを実行します。あらかじめ値が設定されており、問題が悪化したサービスや低下したサービスの背後にあるオペレーションにはapm-spans-aggregate、サイレント状態のサービスが停止した時点の特定にはapm-spans-sparkline、エラースパンの検索にはquery-apm-spansを使います。apm-spans-aggregateの行や、サービスごとにapm-spans-countを 1 回ずつ呼び出した結果から、サービスのサマリーを作成しないでください。オペレーション行のパーセンタイルを合算しても、サービスのパーセンタイルにはなりません。
日次のサービス健全性レポート
dateRange: {"date_from": "-24h"}およびcompare: "previous_day"を指定してapm-service-healthを実行します。続いてnextの呼び出しを使い、健全でない各サービスの背後にあるオペレーションを調べます。okではないサービスについて、新しいエラーパターンを調べるにはlogs-patterns-diffを使います。- 対象期間中に発生したアラートを調べるには
logs-alerts-listを使います。
「時間はどこにかかっていますか?」
apm-trace-getの各スパンにはself_time_nanoが含まれます。これは子スパンに含まれない時間です。この値で並べ替えると、実際の実時間が最もかかったスパンが先頭に表示されます。self_time_nanoが大きい親スパンは、計装されていない区間です(処理はそのスパン内で行われ、記録された子スパンには現れていません)。print_summary.pyを実行すると、duration_nanoに基づいて最も遅い上位 5 件のスパンが表示されます。- ノイズの多いトレースでは、
print_timeline.pyを実行してインデント付きの期間を確認します。時間の大半が 1 つの子スパンにかかっているのか、多数のスパンへのファンアウトにかかっているのかがわかります。 - 遅いスパンを 1 つ詳しく調べるには、
SPAN="<name>" python3 scripts/extract_span.py FILEを実行します。 - 「どの子スパンが支配的か」という集計上の質問には
apm-spans-treeを使い、calls_per_parent_invocationを確認します。これにより、呼び出しごとに遅い子スパンと、親スパン 1 回あたり 20 回実行されるだけの子スパンを区別できます。
「エラーはどこで発生しましたか?」
print_summary.pyは、status_code == 2(Error)のすべてのスパンを一覧表示します。各項目にはサービス、スパン名、親のコンテキストが表示されます。- エラースパンから
parent_span_idをたどってツリーをさかのぼると、そこに至ったリクエスト経路を確認できます。 - エラーの詳細は各スパンの
attributesマップ(例:exception.message、exception.type)にあり、トレースのペイロードに含まれているため、エラースパンから直接読み取れます。apm-attribute-values-listはスパン全体から値を調べるためのものであり、単一スパンの属性を読むために事前に必要なものではありません。
「リクエストはサービス X に到達しましたか?」
print_summary.pyを実行すると、トレースに含まれるサービスの一覧が出力されます。- サービス X が表示されない場合、リクエストはそこに到達していません(または計装が不足しています)。最近 X がスパンを出力したか、いつ停止したかを確認するには、
serviceNames: ["X"]を指定してapm-service-healthを確認します。
「異常なスパンで多く見られる値は何ですか?」(特定の値に偏っている場合)
- 異常なデータに対象を絞ります。
filterGroupでstatus_code = Errorを指定するか、durationのしきい値を設定します。 apm-attributes-listで候補となるキーを見つけます。典型的な候補はserver.address、http.response.status_code、db.system、k8s.pod.nameやservice.versionなどのリソースキーです。- 異常なデータに対して、候補キーごとに
apm-attribute-breakdownを実行します。countの大半を占める値が、その特徴を示しています。 - 偏りを確認します。異常なデータのフィルターを外して再実行するか、各行の
error_count / countを比較します。エラーの95%を占める値がトラフィックの10%にしか現れないなら、それが原因です。エラーとトラフィックの両方で95%を占めるなら、単にトラフィック量が多いだけです。
「いつ急増しましたか?」(時間の経過に伴う傾向)
- フィルターを指定して
apm-spans-sparklineを実行し、時間バケットごとの合計数を取得します(ゼロ埋めされ、期間に応じて約50個のバケットが作成されます)。 - 同じ呼び出しで
statusCodes: [2]を指定し、バケットごとのエラー数を取得します。 - バケットごとのエラー率を「エラー数 / 合計数」で計算します。比率が急上昇したバケットが、急増の始まりです。
- 範囲を絞り込みます。そのバケットの前後に
dateRangeを絞って再実行し、query-apm-spansで生のスパンを取得します。
「レイテンシ分布はどのようになっていますか?」
apm-spans-duration-histogramを実行し、ROOT スパンの対数スケール(1-2-5 系列)の継続時間バケットごとにトレース数を取得します。- 2つ目の山や太い裾があれば、別個の低速データ群を示しています。その
bucket_nsの範囲を確認します。 durationフィルター(ナノ秒)とorderBy: "duration"を指定してquery-apm-spansを実行し、実際に遅いトレースを取得します。
「ファンアウトは適切でしたか?」
print_timeline.pyでインデントを確認します。幅の広いツリーは並列呼び出し、深いツリーは逐次的な依存関係を示します。Client(3)種別のスパンと、その呼び出し先サービスの対応するServer(2)種別のスパンを探します。これは同期的な下流呼び出しです。
属性で検索する(例:http.method=POST)
各スパンには attributes マップがあり、http.method や db.statement などのスパンレベルの OTel 属性がペイロードに含まれています。すでに取得済みのスパンであれば、その値を確認するだけです。リソース属性(k8s ラベルや service.version)はペイロードに含まれません。属性でデータセット全体をフィルタリングするには、次の手順に従います。
apm-attributes-list/apm-attribute-values-listを使って、キーと値を確認します(特にリソース属性)。span_attributeまたはspan_resource_attribute型のfilterGroupエントリを指定して、query-apm-spansを再実行します。
UI リンクの作成
apm-trace-get は、トレース UI でトレースを開くディープリンク _posthogUrl を返します。PostHog UI で確認できるよう、必ずユーザーに提示してください。
query-apm-spans は _posthogUrl を返しません。
クエリツールで見つけたトレースにリンクするには、その trace_id を apm-trace-get に渡し、応答から _posthogUrl を提示します。
PostHog URL を手動で作成しないでください。
トレースの検索
posthog:query-apm-spans を使ってスパンを検索し、フィルタリングします。これはツリーではなくスパンを返す点に注意してください。query.traceId を渡すか、結果から trace_id を取得して apm-trace-get に渡し、ツリーを取得します。
フィルタリングの前に確認する
フィルターを作成する前に、プロジェクトで実際に利用されている情報を確認します。
- サービスの存在を確認する —
apm-services-listを呼び出し、スパンを送信しているサービスを確認します。 - フィルタリング可能な属性を見つける —
attribute_type: "span"または"resource"を指定してapm-attributes-listを呼び出します。 - 実際の値を取得する — キーを指定して
apm-attribute-values-listを呼び出し、実際に使われている値を確認します。
その後で query-apm-spans のフィルターを作成します。カスタム属性はプロジェクトごとに異なるため、推測できません。
フィルターを使う
posthog:query-apm-spans
{
"query": {
"serviceNames": ["api-gateway"],
"dateRange": {"date_from": "-1h"},
"filterGroup": [
{"key": "http.status_code", "operator": "gt", "type": "span_attribute", "value": "499"}
]
}
}
トレース ID を使う(判明している場合)
posthog:apm-trace-get
{
"trace_id": "0123456789abcdef0123456789abcdef"
}
よくある注意点
- 期間の単位はナノ秒です。 1 秒 =
1_000_000_000です。query-apm-spansのdurationのフィルター値もナノ秒です。 status_code == 2は Error です。0は Unset、1は OK です。UI のフィルターで{0, 1}に一致させるにはOKを使用します。kindは 0~5 の整数です。0 は Unspecified、1 は Internal、2 は Server、3 は Client、4 は Producer、5 は Consumer です。- ルートスパンの
parent_span_idは null ではなく、"0000000000000000"(16 個のゼロの 16 進数文字列。8 バイトのスパン ID の幅に一致するもので、16 バイトのトレース ID の幅ではありません)です。
大きなトレース結果の解析
トレースツールの結果は JSON です。インラインで読むには大きすぎる場合、Claude Code はファイルに保存します。
保存されたファイルの形式
[{ "type": "text", "text": "{\"results\": [...], \"_posthogUrl\": \"...\"}" }]
scripts/ 内のすべてのスクリプトは、解析前にこのエンベロープを展開します。
トレース JSON の構造
results (array of span dicts)
└── each span:
├── uuid, trace_id, span_id, parent_span_id (hex strings)
├── name, kind (int 0–5), service_name
├── status_code (int 0–2), is_root_span (bool)
├── timestamp, end_time (ISO 8601)
├── duration_nano (int, nanoseconds)
├── attributes (map of span-level OTel attributes, e.g. db.statement, http.url)
└── matched_filter (0/1 — 1 if this span matched the query-apm-spans filter, 0 if it
only shares a trace with a match; always present, only meaningful from query-apm-spans)
利用可能なスクリプト
| スクリプト | 用途 | 使用方法 |
|---|---|---|
print_summary.py | トレースのメタデータ、サービス、最も遅いスパン、エラー | python3 scripts/print_summary.py FILE |
print_timeline.py | parent_span_id をたどった DFS インデント付きツリー | python3 scripts/print_timeline.py FILE |
extract_span.py | 名前に一致するスパンの全行と親/子 | SPAN="name" python3 scripts/extract_span.py FILE |
search_spans.py | 名前、service_name、ID からキーワードを検索 | SEARCH="kw" python3 scripts/search_spans.py FILE |
show_structure.py | 値を表示せずに JSON のキーと型を表示 | python3 scripts/show_structure.py FILE |
ヒント
query-apm-spansでは必ずdateRangeを設定してください。時間範囲を指定しないクエリは遅くなります。デフォルトは-1hです。必要な場合にのみ範囲を広げてください。- ユーザーがトレースをクリックして開けるよう、回答には必ず
apm-trace-getの_posthogUrlを含めてください。 - スパンレベルの属性は、
apm-trace-get/query-apm-spansのペイロードに含まれています(各スパンのattributesマップ)。リソース属性は含まれていません。これらにはapm-attributes-list(typeresource)とapm-attribute-values-listを使用してください。 - トレースのエントリーを見つけるには、
is_root_spanを使うのが簡単です。00000000…との文字列一致は避けてください。 - 集計(操作ごとの p95、スパンの最も遅い子スパン)には、フラット表示なら
apm-spans-aggregate、親→子のエッジならapm-spans-treeを使用してください。SQL に頼らないでください。