mcpField
E
exploring-apm-traces

exploring-apm-traces

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

exploring-apm-traces について

PostHog に取り込まれた OpenTelemetry のアプリケーションパフォーマンス監視トレースを調べるスキルです。開発者は PostHog の APM ツールと解析スクリプトで、サービスの健全性、トレースツリー、レイテンシ、エラー、スパン属性を確認できます。

主な機能

  1. 01ベースラインとのサービス健全性比較
  2. 02スパンの検索と絞り込み
  3. 03トレース全体のスパン一覧取得
  4. 04操作と呼び出しツリーの集計
  5. 05所要時間の分布と推移の確認
  6. 06スクリプトによる大規模トレース解析

活用シーン

  1. 01健全性が低下したサービスの特定
  2. 02エラーの経路とコンテキストの追跡
  3. 03遅いスパンや下流呼び出しの特定
  4. 04時間経過に伴うエラー率の比較

MCP 対応

PostHog MCP ツールを使い、OpenTelemetry のスパン、トレース、サービスの健全性、属性を検索・分析します。APM トレース、AI オブザーバビリティのトレース、ログを区別します。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/posthog/ai-plugin --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 に頼らないでください。

コメント