investigating-error-issue について
個別のエラートラッキング課題を調査するPostHogスキルです。課題の詳細、サンプル例外、分析クエリ、ログ、関連するセッションリプレイを組み合わせ、状況や原因候補を把握します。
主な機能
- 01課題の状態と集計値の確認
- 02サンプル例外とスタックの調査
- 03リリースやフラグ別のSQL分析
- 04周辺イベントとコンソールログの確認
- 05OTELログの検索
- 06関連リプレイの検索
活用シーン
- 01エラー急増の調査
- 02リリースとエラーの関連確認
- 03例外前後のユーザー操作の追跡
- 04フィーチャーフラグ仮説の検証
MCP 対応
課題クエリ、SQL実行、ログ、セッション録画検索などのPostHogツールを使用します。READMEに記載されたツールはMCPサーバー経由で利用します。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ posthog/ skills --skill investigating-error-issueインストール後、Claude Code で/investigating-error-issueを入力して読み込まれたか確認します。
詳細
エラートラッキングの問題を調査する
ユーザーから「このエラーで何が起きているのですか?」と聞かれたり、問題の URL が貼られたりした場合は、ユーザーが手作業で集める必要のあるコンテキストを収集します。誰に発生しているのか、何が変わったのか、どこで起きているのか、リプレイで原因が確認できるかを調べます。
利用可能なツール
| ツール | 目的 |
|---|---|
posthog:query-error-tracking-issue | コンパクトな問題の詳細(ステータス、担当者、トップフレーム、リリース、集計値) |
posthog:query-error-tracking-issue-events | サンプリングされた $exception イベントとスタック、URL、ブラウザー、$session_id |
posthog:execute-sql | 内訳、リリース/フラグとの相関、エラー前後のイベントとコンソールログ |
posthog:query-logs | サーバー側の問題について、エラー発生時刻前後の OTEL ログエントリ |
posthog:query-session-recordings-list | 関連するリプレイ(ランキングは finding-replay-for-issue に委任) |
posthog:read-data-schema | 絞り込みに使う前にプロパティキーを確認 |
ワークフロー
ステップ 1 — 問題の基本情報を確認する
コンパクトな集計値とスパークラインを含めて、問題のレコードを取得します。
posthog:query-error-tracking-issue
{
"issueId": "<issue_id>",
"dateRange": { "date_from": "-30d" },
"includeSparkline": true,
"volumeResolution": 12
}
次の情報を記録します。name、description、status、first_seen、last_seen、assignee、
合計の occurrences/users/sessions、アプリ内のトップフレーム、最新リリースのメタデータ、ボリュームのバケットです。
スパークラインから、横ばい、急増、段階的な増加、または再発のどの傾向かがわかります。この傾向に基づいて、以降の調査方法を決めます。ユーザーがステータスだけを尋ねている場合は、トークンを節約するため includeSparkline を省略します。
ステップ 2 — 例外イベントのサンプルを取得する
キャプチャされたイベントには、原因を推測するために必要なスタックフレーム、URL、ブラウザー、プロパティが含まれています。まず最近のサンプルを取得し、次に初期のサンプルを取得して比較します。
posthog:query-error-tracking-issue-events
{
"issueId": "<issue_id>",
"limit": 1,
"include": ["exception", "stacktrace", "environment", "navigation", "correlation"]
}
このツールはデフォルトで onlyAppFrames: true に設定されており、ベンダーフレームを除外します。問題がサードパーティーライブラリにありそうな場合は false に切り替えます。また、stacktrace.type: "resolved" が返されたのにフレームがまったくない場合も切り替えます(すべてのフレームがリゾルバーによってベンダー由来と判断される、ミニファイされたバンドルでよく起こります。例:React の本番ビルド)。
最も古いサンプルを取得するには、dateRange を問題の first_seen 前後の狭い期間に設定します(例:date_from を少し前に、date_to を少し後に設定)。さらに orderDirection: "ASC" を渡すと、期間内で最新のイベントではなく最も古いイベントを取得できます。ツールのデフォルトは DESC で、最近のイベントが返され、最初の呼び出しと重複してしまうためです。
最近のイベントと最も古いイベントに明確な違いがある場合(スタックのルートや URL のパターンが異なる場合など)、問題のグルーピングが誤っている可能性があります。ひとつのバグとして調査を続けるのではなく、grouping-noisy-errors に報告してください。
ステップ 3 — 内訳を実行して原因を特定する
内訳用の型付きツールはありません。execute-sql を使用します。問題の傾向から必要な内訳だけを実行してください。各内訳でクエリが必要になり、統合結果が煩雑になるためです。
| スパークラインの傾向 | 最初に試す内訳 |
|---|---|
| ゼロから急増 | アプリのバージョン/リリース別 — ほとんどの場合、デプロイによるリグレッションです(下記参照) |
| 高い状態で安定 | ブラウザー/OS 別 — レンダリングまたはプラットフォーム固有のバグ |
| 段階的な増加 | 地域または機能フラグ別 — 段階的なロールアウトによる影響 |
| 急増の後に静穏 | 時刻または $current_url 別 — スケジュールされたジョブまたは特定のページ |
適切なバージョンプロパティの選び方
PostHog は、バージョンのような値を持つフィールドを3つ出力します。それぞれ意味が異なり、「ユーザーのアプリのどのバージョンでこれが導入されたか」という問いに答えられるのは、そのうち1つだけです。
| プロパティ | 内容 | 自動取得される条件 | 用途 |
|---|---|---|---|
$exception_releases | Cymbal が管理するリリースマップ。リリース ID をキーにします | SDK がリリースメタデータを公開する場合のみ(例:リリースに関連付けられたソースマップのアップロード) | リリースの帰属を最も正確に特定できます。値がある場合に限ります |
$app_version | ユーザーがデプロイしたアプリのバージョン | iOS(CFBundleShortVersionString)、React Native(Expo / react-native-device-info) | 「アプリのどのデプロイでこれが導入されたか」——ユーザーが知りたいこと |
$lib_version | PostHog SDK ライブラリのバージョン(例:posthog-js 1.298.0) | すべてのイベントで、すべての SDK が取得 | 「PostHog SDK のアップグレードでこれが導入されたか」という限定的な問い |
$lib_version はほぼすべてのイベントにあるため、使いたくなりますが、これは PostHog ライブラリのバージョンであり、ユーザーのアプリのバージョンではありません。$lib_version が一定のまま急増している場合、SDK は変更されておらず、ユーザーが自身のコードに不具合を含む変更をリリースしたことを意味します。これはよくあるケースです。$lib_version を使うのは、ほかの値が何も設定されておらず、「PostHog のアップグレードが原因か」と明示的に確認したい場合に限ります。
Web/サーバー/Node/Java/Python のプロジェクトでは、$app_version は自動取得されません。顧客が(register、コンテキストプロバイダー、または before_send を使って)設定する必要があります。ブレークダウンの結果で $app_version の行がすべて NULL の場合は、分析結果にその旨を明記し、顧客に設定を追加するよう提案してください。代わりに $exception_releases を使うか、first_seen による日単位のタイムラインを使えば、調査を進められます。
例($app_version — モバイルでは自動取得され、Web/サーバーでは手動で設定します):
posthog:execute-sql
SELECT
properties.$app_version AS app_version,
count() AS occurrences,
uniq(person_id) AS users,
min(timestamp) AS first_seen,
max(timestamp) AS last_seen
FROM events
WHERE event = '$exception'
AND (issue_id = '<issue_id>' OR properties.$exception_issue_id = '<issue_id>')
AND timestamp > now() - INTERVAL 30 DAY
GROUP BY app_version
ORDER BY occurrences DESC
LIMIT 20
(issue_id = ... OR properties.$exception_issue_id = ...) のパターンは、イベントとイシューを関連付ける2つの方法をどちらもカバーします。issue_id は events 上の解決済み仮想フィールドであり、フィンガープリントの上書きに追従して、結合または分割されたイシューを正しく振り分けます。一方、properties.$exception_issue_id は取り込み時に取得された、イベントの未加工プロパティです。プロパティだけでフィルタリングすると、結合または分割されたイシューのイベント数が気付かないうちに少なくなります。
ある app_version の first_seen がイシュー全体の first_seen よりかなり遅い場合、そのリリースでバグが導入または悪化した可能性があります。これは根本原因を示す強い兆候です。すべての行が NULL の場合、このプロジェクトでは SDK がアプリのバージョンを報告していません(Web/サーバーでよくあります)。顧客がリリースを出荷している場合は $exception_releases に切り替え、そうでなければ toDate(timestamp) のタイムラインを使ってください。
$exception_releases に値がある場合、これはリリース ID をキーとする JSON ディクショナリです。トップレベルの $release プロパティはありません。リリースの帰属を特定する必要があり、顧客が設定済みの場合は、$exception_releases を直接クエリしてください。
properties.$browser、properties.$os、properties.$current_url、またはプロジェクトでエラーにタグ付けする任意のフィーチャーフラグを使って、同様に繰り返してください。
ステップ 4 — フィーチャーフラグの公開状況を確認する
実験やロールアウトが疑われる場合は、エラー発生時に影響を受けたユーザーでフラグが有効になっていたかを確認します。
影響を受けたユーザーに対して評価されたフラグを列挙するには、$active_feature_flags プロパティを解析します。これは ClickHouse では Nullable(String) 型の JSON エンコードされたカラムとして実体化されるため、arrayJoin(properties.$active_feature_flags) を直接実行すると失敗します。使用できる方法は JSONExtract ですが、最初に引数を null 非許容の String に変換する必要があります。ClickHouse は入力が Nullable の場合、JSONExtract からネストした Array(String) を返すことを拒否します。この型エラーはクエリ計画時に発生するため、WHERE 句の notEmpty(...) ガードでは防げません。引数を ifNull(..., '[]')(または assumeNotNull(...))でラップしてください。
posthog:execute-sql
SELECT
arrayJoin(JSONExtract(ifNull(toString(properties.$active_feature_flags), '[]'), 'Array(String)')) AS flag,
count() AS occurrences,
uniq(person_id) AS users
FROM events
WHERE event = '$exception'
AND (issue_id = '<issue_id>' OR properties.$exception_issue_id = '<issue_id>')
AND timestamp > now() - INTERVAL 14 DAY
AND notEmpty(toString(properties.$active_feature_flags))
GROUP BY flag
ORDER BY occurrences DESC
LIMIT 20
注意点:各イベントには評価されたすべてのフラグキーが記録されるため、この列挙ではフラグ間で同じ件数が返されることがよくあり、どのフラグがエラーと相関しているかはわかりません。わかるのは、ユーザーに対してどのフラグが有効だったかだけです。仮説を実際に検証するには、評価値(true/false/バリアント名)が格納された、フラグごとの値カラム properties.$feature/<flag-key> をクエリします。
posthog:execute-sql
SELECT
properties.`$feature/my-flag-key` AS variant,
count() AS occurrences,
uniq(person_id) AS users
FROM events
WHERE event = '$exception'
AND (issue_id = '<issue_id>' OR properties.$exception_issue_id = '<issue_id>')
AND timestamp > now() - INTERVAL 14 DAY
GROUP BY variant
ORDER BY occurrences DESC
ここでのバリアントの内訳を、同じ期間におけるプロジェクト全体の同じフラグの公開状況と比較します。いずれかのバリアントが不均衡に多く含まれていれば、そのフラグが原因に関与している可能性があります。確証ではありませんが、有力な仮説です。
ステップ 5 — エラー前後に何が起きたかを再構成する
ステップ 2 のサンプルイベントから $session_id を使って、例外前後のアクティビティを取得します。3 つのソースを組み合わせて確認できます。エラーを記録した SDK に応じて、該当するクエリを実行してください。
5a. 前後のイベント($session_id を使うクライアント SDK)
ET のフロントエンドのセッションタイムラインと同様のものです。同じセッション内で例外発生の前後 ±1 時間以内に記録されたカスタムイベント、ページビュー、その他の例外を取得します。
posthog:execute-sql
SELECT
uuid,
event,
timestamp,
properties.$lib AS lib,
properties.$current_url AS url
FROM events
WHERE $session_id = '<session_id_from_step_2>'
AND (event = '$exception' OR event = '$pageview' OR left(event, 1) != '$')
AND timestamp >= toDateTime('<error_timestamp>', 'UTC') - INTERVAL 1 HOUR
AND timestamp <= toDateTime('<error_timestamp>', 'UTC') + INTERVAL 1 HOUR
ORDER BY timestamp ASC
LIMIT 100
left(event, 1) != '$' 句は PostHog の自動キャプチャ/システムイベントを除外しつつ、すべてのカスタムイベントを保持します。例外の OR event = '$pageview'/'$exception' によって、タイムラインで確認する価値のある 2 つのシステムイベントが再び含まれます。これは ET UI と同じフィルターです。
出力内で $lib の値が混在しているのは、ノイズではなく有用な情報です。サーバー SDK がクライアントリクエストから $session_id を伝播すると(PostHog 自体のバックエンドもこれを行います)、タイムラインではブラウザー側のアクティビティと並んでサーバー側のアクティビティも表示されます。つまり「利用可能であれば両方の SDK」の情報を追加作業なしで取得できます。lib カラムをざっと確認すると、各行がどのように生成されたかがわかります。
このスキルでは、UI に合わせてデフォルトで前後 ±1 時間を対象にします。問題に関連する処理に時間がかかる場合(長時間のバッチジョブやバックグラウンドワーカーなど)は範囲を広げ、例外発生直前の数秒だけが重要な場合は範囲を狭めてください。
5b. コンソールログ(Web/React Native のセッションリプレイ)
セッションリプレイが有効になっている場合、リプレイのパイプラインは同じセッション ID のタグが付いた console.* 呼び出しを log_entries テーブルに出力します。同じ期間を指定して取得します。
posthog:execute-sql
SELECT timestamp, level, message
FROM log_entries
WHERE log_source = 'session_replay'
AND log_source_id = '<session_id_from_step_2>'
AND timestamp >= toDateTime('<error_timestamp>', 'UTC') - INTERVAL 1 HOUR
AND timestamp <= toDateTime('<error_timestamp>', 'UTC') + INTERVAL 1 HOUR
ORDER BY timestamp ASC
LIMIT 200
log_source = 'session_replay' が識別子です。log_entries は他のソースと共有されています。結果が空になることはよくあります。リプレイが有効になっていないか、対象のセッションが記録されていない可能性があります。これを失敗として扱うのではなく、分析結果のまとめにその旨を記載してください。
5c. エラー発生前後のサーバーログ(query-logs 経由の OTEL)
サーバー側の例外については、例外のタイムスタンプと、顧客が取り込んでいる OTEL ログエントリを関連付けます。ログをまったく取り込んでいないプロジェクトも多いため、query-logs の結果がない場合やエラーになる場合は、その旨を伝えて次に進みます。どのサービスがエラーを出したのかわからない場合は、まず logs-attribute-values-list で利用可能なサービスを確認します。
posthog:query-logs
{
"query": {
"dateRange": {
"date_from": "<error_timestamp minus 5 minutes>",
"date_to": "<error_timestamp plus 5 minutes>"
},
"severityLevels": ["error", "warn"],
"serviceNames": ["<service.name if known>"],
"limit": 50,
"orderBy": "earliest"
}
}
この出力を利用する前に知っておきたい注意点:
- ログはイベントとは別に取り込まれ、通常は保持期間が短くなっています。 問題が引き続き発生していても、古い例外は結果が空になることがあります。
trace_id/span_idは未設定の場合、ゼロ埋めされた値("00000000...")が返されます。 トレースベースの関連付けが機能するのは、明示的に計測されたリクエストのみであり、 すべてのイベントで機能するわけではありません。service.nameはリソース属性です。発生元がわかっている場合は、 フリーテキストのsearchTermではなくserviceNamesで絞り込みます。
5d. 代表的なリプレイを見つける
最適なセッションの選定が重要な場合は、finding-replay-for-issue に引き継ぎます。
人気のある問題には何百もの録画が紐付いていることがあり、その多くは短いクラッシュの断片や
アイドル状態のタブのセッションです。このスキルでは、原因を示している可能性が最も高い録画を
見つけるために、長さ/アクティブ時間/新しさでランキングします。どのリプレイか指定せずに
ユーザーが「リプレイを見たい」と尋ねた場合も、引き継ぎます。
紐付いているセッションが少数だけの場合、ユーザーが特定のセッションをすでに指定している場合、
または動作する例であれば何でもよい場合(エラーが再現することを示す場合など)は、
引き継ぎを省略し、手順 2 ですでに取得したサンプル例外イベントの session_ids を使って
query-session-recordings-list から録画を取得します。
どちらの方法でも録画が取得できない場合は、影響を受けたユーザーに対してセッションリプレイが 有効になっていない可能性があると伝えます。これは失敗ではなく、有用な背景情報です。
手順 6 — 要約する
次の順序で提示します。
- 何が起きているか — 種類、メッセージ、スタック内の発生箇所
- 誰に影響しているか — ユーザー数、セッション数、目立ったセグメント別の内訳
- いつ始まったか —
first_seenと、内訳から判明した場合は原因となったリリース/バージョン - 考えられる原因 — 上記の内訳に裏付けられた仮説を 1 つか 2 つ
- 次のステップ — 疑わしいリリースの調査、紐付いたリプレイの確認、担当者への連絡、エスカレーションなど、具体的な行動
要約は簡潔にします。ユーザーが求めているのは答えであり、データの案内ではありません。
ヒント
- イベントから問題への標準的な結合キーは、解決済みの
issue_id仮想フィールドです。フォールバックとしてproperties.$exception_issue_idを使います。 理由については手順 3 を参照してください。 - 「どのバージョンで導入されたか?」を内訳で調べる場合は、
$app_version(ユーザーが デプロイしたアプリのバージョン。iOS/React Native では自動取得され、Web/サーバーでは 手動で設定されます)または、値が設定されている場合は$exception_releasesを優先します。 この質問には$lib_versionを使わないでください。これは PostHog SDK のライブラリバージョンであり、 ユーザーのアプリのバージョンではありません。手順 3 の「適切なバージョンプロパティの選択」 サブセクションを参照してください。 - 問題が 30 日を超えて続いている場合は、日付範囲を明示的に広げます。
デフォルトでは、内訳から最初に発生した
first_seenイベントが除外されることがよくあります。 - サンプルのスタックから原因が明らかな場合を除き、要約で修正案を提示しないでください。 データに裏付けられた仮説のほうが、確信めいた推測よりも役に立ちます。
query-error-tracking-issueがexternal_issues配列を返した場合、その問題はすでに Linear/Jira/GitHub のチケットに紐付いています。ユーザーが重複して作成しないよう、 要約でリンクに触れてください。
関連スキル
triaging-error-issues— 1 つの問題から視点を広げ、どの未解決問題を優先すべきかランキングするfinding-replay-for-issue— この問題の例外に紐付いたセッション録画を確認するgrouping-noisy-errors— 誤ってグループ化されたフィンガープリントによって問題にノイズが混じっている場合や、イベントが重複した問題に分散している場合に使用するauthoring-error-tracking-alerts— この種の問題の新規作成、再オープン、急増時に通知を受け取る