关于 investigating-error-issue
用于调查单个错误追踪问题的 PostHog 技能。它结合问题详情、抽样异常、分析查询、日志和关联会话回放,帮助了解错误背景并查找可能原因。
主要功能
- 01查看问题状态和汇总指标
- 02检查抽样异常与堆栈
- 03按发布版本和标记执行 SQL 拆分
- 04查看周边事件与控制台日志
- 05查询 OTEL 日志
- 06查找关联回放
适用场景
- 01调查错误量突然激增
- 02检查错误是否与某次发布相关
- 03追踪用户异常前后的活动
- 04验证功能标记假设
MCP 相关
该技能使用 PostHog 工具,包括问题查询、SQL 执行、日志和会话录制查询。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,会移除供应商堆栈帧;如果问题似乎出在第三方库中,或者响应返回 stacktrace.type: "resolved",但完全没有堆栈帧(这在压缩后的 bundle 中很常见,因为解析器会将每个堆栈帧都识别为供应商代码,例如 React 生产构建),请将其切换为 false。
要获取最早的样本,请将 dateRange 缩小到问题 first_seen 附近的时间范围(例如,将 date_from 设为略早的时间,将 date_to 设为略晚的时间),并传入 orderDirection: "ASC",这样就能获取该时间范围内最早的事件,而非最新事件。该工具默认使用 DESC,会返回近期事件,从而在不提示的情况下重复首次调用。
如果近期事件和最早事件存在实质差异——例如堆栈根不同、URL 模式不同——问题可能是分组错误。请标记给 grouping-noisy-errors,不要继续将其视为同一个 bug 进行调查。
步骤 3 — 运行细分查询以定位原因
细分查询不是类型化工具,请使用 execute-sql。只运行问题形态所提示的细分查询;每个查询都会占用资源,也会让综合分析变得杂乱。
| 迷你趋势图形态 | 首先尝试的细分方式 |
|---|---|
| 从零开始激增 | 按应用版本 / 发布版本细分——几乎总是部署回归问题(见下文) |
| 高位平稳 | 按浏览器 / OS 细分——渲染或平台特定问题 |
| 逐渐上升 | 按地理位置或功能标记细分——逐步推出功能导致的影响 |
| 多次突发后归于平静 | 按一天中的时段或 $current_url 细分——定时任务或特定页面 |
选择合适的版本属性
PostHog 会生成 3 个格式类似版本号的字段。它们的含义各不相同,只有 其中一个能回答“用户应用的哪个版本引入了这个问题?”:
| 属性 | 含义 | 自动采集来源 | 用途 |
|---|---|---|---|
$exception_releases | 由 Cymbal 管理、以发布 ID 为键的发布映射 | 仅当 SDK 发布发布元数据时(例如,与某个发布关联的 sourcemap 上传) | 存在时,可实现最精确的发布归因 |
$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 未变更的情况下,在自己的代码中发布了回归问题;这才是常见情况。只有当其他
字段都没有值,且你明确要问“是不是升级 PostHog 导致了这个问题?”时,才使用
$lib_version。
Web / server / Node / Java / Python 项目不会自动采集
$app_version——客户必须自行设置(通过 register、上下文
提供程序或 before_send)。如果细分结果只有一行 $app_version 且值全为 NULL,
请在综合分析中明确说明,并建议客户配置该字段;改用 $exception_releases,或按
first_seen 查看每日时间线,都能让调查继续推进。
示例($app_version——移动端自动填充,Web / server 需手动设置):
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 = ...) 模式
涵盖了事件与问题关联的两种方式。issue_id 是 events 上解析后的虚拟字段
(它会遵循指纹覆盖规则,以便正确路由合并或拆分后的问题);properties.$exception_issue_id
是摄取时采集的原始事件属性。仅按该属性筛选,会导致合并或拆分过的问题所对应的事件
被悄悄漏计。
如果某个 app_version 的 first_seen 比问题整体的 first_seen 晚很多,那么该
版本可能引入或加剧了错误——这是有力的根因信号。如果每一行都是 NULL,说明此项目的
SDK 没有报告应用版本(Web / server 项目中很常见)——如果客户会发布版本,则改用
$exception_releases;否则退回到 toDate(timestamp) 时间线。
$exception_releases 有值时,它是一个以发布 ID 为键的 JSON 字典。不存在顶层
$release 属性;如果需要发布归因且客户已配置该字段,请直接查询 $exception_releases。
也可以按 properties.$browser、properties.$os、properties.$current_url,
或项目用于标记错误的任何功能标志重复查询。
Step 4 — 检查功能标志曝光情况
如果用户怀疑某项实验或逐步发布,请检查错误发生时受影响的用户是否启用了某个标志。
要枚举受影响用户评估过的标志,请解析 $active_feature_flags 属性——它在 ClickHouse 中会实体化为 Nullable(String) 类型的 JSON 编码列,因此直接使用 arrayJoin(properties.$active_feature_flags) 会失败。可用的模式是 JSONExtract,但必须先将参数强制转换为非空 String:如果输入是 Nullable,ClickHouse 会拒绝从 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
注意:每个事件都会记录所有评估过的标志键,因此此枚举通常会为每个标志返回相同的计数,无法告诉你哪个标志与错误相关——只能说明这些标志对用户是否启用。要实际验证某个假设,请查询每个标志的取值列 properties.$feature/<flag-key>,该列包含评估结果(true/false/变体名称):
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
将此处的变体分布与同一时间窗口内项目在同一标志上的整体曝光情况进行比较。某个变体占比明显偏高,说明该标志可能与问题原因有关——这并非定论,但属于有力的假设。
Step 5 — 重建错误发生前后的情况
使用步骤 2 中示例事件的 $session_id,提取异常前后的活动记录。以下三个来源可以相互补充;请根据捕获错误的 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' 例外条件会重新纳入时间线上值得查看的两个系统事件。这与 ET UI 使用的筛选条件相同。
输出中出现不同的 $lib 值是特性,而非噪声。当服务端 SDK 从客户端请求中传递 $session_id 时(PostHog 自己的后端也会这样做),时间线会将服务端活动内联显示在浏览器活动旁边——无需额外操作,就能在有数据时同时看到两个 SDK 的记录。查看 lib 列即可了解每行记录由何种方式生成。
此技能默认使用 ±1 小时的时间窗口,因为 UI 采用的也是这一设置;如果问题涉及耗时较长的操作(长时间批处理作业、后台工作进程),请扩大窗口;如果只需查看抛出错误前几秒的情况,则缩小窗口。
5b. 控制台日志(网页 / React Native 会话回放)
启用会话回放后,回放管道会将 console.* 调用写入 log_entries 表,并标记相同的会话 ID。请使用匹配的时间窗口提取日志:
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是资源属性。已知产生日志的服务时,请使用serviceNames缩小范围,而不是使用自由文本searchTerm。
5d. 查找有代表性的回放
如果挑选_最佳_会话很重要,请交由 finding-replay-for-issue 处理——热门问题会关联数百条录制内容,其中大多是很短的崩溃片段或闲置标签页会话;该技能会根据时长、活动时间和最近时间进行排序,找出最有可能呈现问题原因的会话。用户只说“找一段回放”但未指定具体会话时,也请交由该技能处理。
如果关联的会话只有少数几个、用户已指定某个会话,或者任何可用示例都足够(例如,用来证明错误可以复现),则跳过交接,直接使用第 2 步已获取的示例异常事件中的 session_ids,通过 query-session-recordings-list 提取录制内容。
如果这两种方式都没有返回录制内容,请说明受影响用户可能未启用会话回放。这是有用的背景信息,并不代表失败。
第 6 步 — 综合分析
按以下顺序呈现:
- 问题是什么——类型、消息,以及在调用栈中的位置
- 影响对象——用户总数、会话总数,以及值得注意的细分群体
- 开始时间——
first_seen,以及细分分析发现的问题引入版本 - 可能原因——根据上述细分分析提出一到两个假设
- 后续步骤——具体行动:调查可疑版本、查看关联回放、联系负责人,或升级处理
综合分析应简明扼要。用户想要的是答案,而不是数据导览。
提示
- 从事件关联到问题的规范联接键是已解析的虚拟字段
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——从单个问题转向对未解决问题进行排序,确定哪些值得关注finding-replay-for-issue——查看与此问题异常关联的会话录制grouping-noisy-errors——问题因错误分组的指纹而受到干扰,或其事件被分散到多个重复项中时使用authoring-error-tracking-alerts——在此类问题创建、重新打开或激增时收到通知