mcpField
E
exploring-apm-traces

exploring-apm-traces

github.com/posthogクラウド・DevOps76361 回インストール今日更新
リポジトリ

exploring-apm-traces について

exploring-apm-tracesは、PostHogが収集したOpenTelemetryの分散トレースを調査するスキルです。専用のAPMツールでサービスの健全性を評価し、レイテンシやエラーを特定してトレースとスパンのデータを確認できます。

主な機能

  1. 01ベースラインとのサービス健全性比較
  2. 02スパン検索とトレース全体の取得
  3. 03レイテンシ、エラー、操作の集計
  4. 04コールツリーとスパン属性の確認
  5. 05スクリプトによる大規模トレース結果の解析

活用シーン

  1. 01健全性が悪化したサービスの特定
  2. 02遅いリクエストの呼び出し経路の追跡
  3. 03トレース内でエラーが発生した場所の特定
  4. 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.md
scripts/
_common.py
extract_span.py
print_summary.py
print_timeline.py
search_spans.py
show_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-get1 つの 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-breakdown1 つの属性値でグループ化したスパン件数
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 進数 ID
  • parent_span_id — 親の span_id を指します(ルートの場合はゼロ埋めされた 16 進数の 000…000)
  • is_root_span — トレースのエントリを示す便宜的なフラグ

ツリーを再構築する手順は次のとおりです。

  1. is_root_span が true のスパン(または parent_span_id == "00000000…" のスパン)がルートスパンです。
  2. それ以外の各スパンは、span_id がその parent_span_id と一致するスパンの子です。
  3. parent_span_id でグループ化し、各ルートから下方向にたどります。

scripts/print_timeline.py がこの処理を行い、深さ優先探索のインデント付きツリーを出力します。

調査パターン

「サービスの健全性はどうですか?」(まずはこちら)
  1. apm-service-health は、1 回の呼び出しですべてのサービスを評価します。リクエスト数、エラー率、p50/p95/p99、ベースライン期間からの変化、status(silent、regressed、degraded、new、recovered、improved、ok)とその reason を確認できます。行は問題が深刻な順に表示されます。
  2. compare でベースラインを選択します。previous_period(デフォルト)、previous_day、previous_week、または query と併せて指定する明示的な baselineDateRange を選べます。environment と serviceNames で対象を絞り込みます。
  3. next にある呼び出しを実行します。あらかじめ値が設定されており、問題が悪化したサービスや低下したサービスの背後にあるオペレーションには apm-spans-aggregate、サイレント状態のサービスが停止した時点の特定には apm-spans-sparkline、エラースパンの検索には query-apm-spans を使います。
  4. apm-spans-aggregate の行や、サービスごとに apm-spans-count を 1 回ずつ呼び出した結果から、サービスのサマリーを作成しないでください。オペレーション行のパーセンタイルを合算しても、サービスのパーセンタイルにはなりません。
日次のサービス健全性レポート
  1. dateRange: {"date_from": "-24h"} および compare: "previous_day" を指定して apm-service-health を実行します。続いて next の呼び出しを使い、健全でない各サービスの背後にあるオペレーションを調べます。
  2. ok ではないサービスについて、新しいエラーパターンを調べるには logs-patterns-diff を使います。
  3. 対象期間中に発生したアラートを調べるには logs-alerts-list を使います。
「時間はどこにかかっていますか?」
  1. apm-trace-get の各スパンには self_time_nano が含まれます。これは子スパンに含まれない時間です。この値で並べ替えると、実際の実時間が最もかかったスパンが先頭に表示されます。self_time_nano が大きい親スパンは、計装されていない区間です(処理はそのスパン内で行われ、記録された子スパンには現れていません)。
  2. print_summary.py を実行すると、duration_nano に基づいて最も遅い上位 5 件のスパンが表示されます。
  3. ノイズの多いトレースでは、print_timeline.py を実行してインデント付きの期間を確認します。時間の大半が 1 つの子スパンにかかっているのか、多数のスパンへのファンアウトにかかっているのかがわかります。
  4. 遅いスパンを 1 つ詳しく調べるには、SPAN="<name>" python3 scripts/extract_span.py FILE を実行します。
  5. 「どの子スパンが支配的か」という集計上の質問には apm-spans-tree を使い、calls_per_parent_invocation を確認します。これにより、呼び出しごとに遅い子スパンと、親スパン 1 回あたり 20 回実行されるだけの子スパンを区別できます。
「エラーはどこで発生しましたか?」
  1. print_summary.py は、status_code == 2(Error)のすべてのスパンを一覧表示します。各項目にはサービス、スパン名、親のコンテキストが表示されます。
  2. エラースパンから parent_span_id をたどってツリーをさかのぼると、そこに至ったリクエスト経路を確認できます。
  3. エラーの詳細は各スパンの attributes マップ(例:exception.message、exception.type)にあり、トレースのペイロードに含まれているため、エラースパンから直接読み取れます。apm-attribute-values-list はスパン全体から値を調べるためのものであり、単一スパンの属性を読むために事前に必要なものではありません。
「リクエストはサービス X に到達しましたか?」
  1. print_summary.py を実行すると、トレースに含まれるサービスの一覧が出力されます。
  2. サービス X が表示されない場合、リクエストはそこに到達していません(または計装が不足しています)。最近 X がスパンを出力したか、いつ停止したかを確認するには、serviceNames: ["X"] を指定して apm-service-health を確認します。
「異常なスパンで多く見られる値は何ですか?」(特定の値に偏っている場合)
  1. 異常なデータに対象を絞ります。filterGroup で status_code = Error を指定するか、duration のしきい値を設定します。
  2. apm-attributes-list で候補となるキーを見つけます。典型的な候補は server.address、http.response.status_code、db.system、k8s.pod.name や service.version などのリソースキーです。
  3. 異常なデータに対して、候補キーごとに apm-attribute-breakdown を実行します。count の大半を占める値が、その特徴を示しています。
  4. 偏りを確認します。異常なデータのフィルターを外して再実行するか、各行の error_count / count を比較します。エラーの95%を占める値がトラフィックの10%にしか現れないなら、それが原因です。エラーとトラフィックの両方で95%を占めるなら、単にトラフィック量が多いだけです。
「いつ急増しましたか?」(時間の経過に伴う傾向)
  1. フィルターを指定して apm-spans-sparkline を実行し、時間バケットごとの合計数を取得します(ゼロ埋めされ、期間に応じて約50個のバケットが作成されます)。
  2. 同じ呼び出しで statusCodes: [2] を指定し、バケットごとのエラー数を取得します。
  3. バケットごとのエラー率を「エラー数 / 合計数」で計算します。比率が急上昇したバケットが、急増の始まりです。
  4. 範囲を絞り込みます。そのバケットの前後に dateRange を絞って再実行し、query-apm-spans で生のスパンを取得します。
「レイテンシ分布はどのようになっていますか?」
  1. apm-spans-duration-histogram を実行し、ROOT スパンの対数スケール(1-2-5 系列)の継続時間バケットごとにトレース数を取得します。
  2. 2つ目の山や太い裾があれば、別個の低速データ群を示しています。その bucket_ns の範囲を確認します。
  3. duration フィルター(ナノ秒)と orderBy: "duration" を指定して query-apm-spans を実行し、実際に遅いトレースを取得します。
「ファンアウトは適切でしたか?」
  1. print_timeline.py でインデントを確認します。幅の広いツリーは並列呼び出し、深いツリーは逐次的な依存関係を示します。
  2. Client(3)種別のスパンと、その呼び出し先サービスの対応する Server(2)種別のスパンを探します。これは同期的な下流呼び出しです。
属性で検索する(例:http.method=POST)

各スパンには attributes マップがあり、http.method や db.statement などのスパンレベルの OTel 属性がペイロードに含まれています。すでに取得済みのスパンであれば、その値を確認するだけです。リソース属性(k8s ラベルや service.version)はペイロードに含まれません。属性でデータセット全体をフィルタリングするには、次の手順に従います。

  1. apm-attributes-list / apm-attribute-values-list を使って、キーと値を確認します(特にリソース属性)。
  2. 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 に渡し、ツリーを取得します。

フィルタリングの前に確認する

フィルターを作成する前に、プロジェクトで実際に利用されている情報を確認します。

  1. サービスの存在を確認する — apm-services-list を呼び出し、スパンを送信しているサービスを確認します。
  2. フィルタリング可能な属性を見つける — attribute_type: "span" または "resource" を指定して apm-attributes-list を呼び出します。
  3. 実際の値を取得する — キーを指定して 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.pyparent_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(type resource)と apm-attribute-values-list を使用してください。
  • トレースのエントリーを見つけるには、is_root_span を使うのが簡単です。00000000… との文字列一致は避けてください。
  • 集計(操作ごとの p95、スパンの最も遅い子スパン)には、フラット表示なら apm-spans-aggregate、親→子のエッジなら apm-spans-tree を使用してください。SQL に頼らないでください。

コメント