关于 grouping-noisy-errors
这是一个 PostHog 技能,用于安全合并重复的错误追踪问题,并防止噪声指纹持续产生新问题。它指导开发者核对调用位置、合并历史问题,并创建范围严格的分组规则。
主要功能
- 01比对异常类型、SDK、堆栈、消息和处理状态
- 02合并已确认的重复问题
- 03创建规则以分组后续事件
- 04检查现有规则并验证结果
适用场景
- 01合并因堆栈或消息动态数据而重复的问题
- 02清理不会再出现的历史重复问题
- 03防止重复错误持续产生新指纹
MCP 相关
使用 PostHog MCP 工具查询问题和事件、合并或拆分问题,以及管理分组规则。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ posthog/ skills --skill grouping-noisy-errors安装后在 Claude Code 中输入/grouping-noisy-errors确认已加载。
详细介绍
对噪声错误进行归组
当堆栈帧或消息中包含易变数据(随机 ID、动态文件路径、构建哈希、匿名函数名称)时,同一个错误可能会被报告为数十个独立问题。解决方法分两步:先将现有问题合并到一个目标问题中,再创建归组规则,让来自同一调用位置的后续事件共享一个规范指纹,而不是不断产生新指纹。
首先要明确:“同一个错误”的定义范围很窄。如果两个问题只是名称相同,或消息文本中有相同的句子,但来自不同代码路径、不同 SDK 或不同运行时,它们就是不同的错误,应保持分开,即使用户认为它们是“同一类 Bug”。因为消息中都包含“undefined”,就把前端的 TypeError 和后端的 TypeError 归为一组,会破坏团队定位各自问题所需的信号。步骤 1 中的标准就是为了避免这种情况。
可用工具
| 工具 | 用途 |
|---|---|
posthog:query-error-tracking-issues-list | 查找可能重复的问题 |
posthog:query-error-tracking-issue | 获取单个问题的简要详情 |
posthog:query-error-tracking-issue-events | 获取带有堆栈和消息的采样 $exception 事件 |
posthog:error-tracking-issues-merge-create | 将现有问题合并到目标问题 |
posthog:error-tracking-issues-split-create | 如果合并出错,将指纹精准拆分回原状 |
posthog:error-tracking-grouping-rules-create | 自动将后续事件归入同一个问题 |
posthog:error-tracking-grouping-rules-list | 添加新规则前检查现有归组规则 |
posthog:error-tracking-issues-partial-update | 合并后重命名目标问题或重新描述它 |
合并与归组规则
这两种工具分别解决问题的不同部分:
- 合并是一次性操作。它会将现有问题合并到一个目标问题中,并重新关联其事件。后续事件仍会按原始指纹归组——如果相同的噪声模式不断产生新指纹,合并就会变成无休止的重复操作。
- 归组规则具有持久性。它会在事件入库时,将任何匹配事件的指纹改写为
custom-rule:<rule_id>,让所有后续匹配事件共享一个规范指纹,而不是产生新指纹。首次匹配时,系统会创建一个以该指纹为键的新问题,或将事件路由到已关联该指纹的问题。
如果问题会反复出现,请同时使用两者:将历史重复问题合并到目标问题中,然后创建规则。规则 API 不接受目标问题 ID——规则开始生效后,可以将生成的 custom-rule:... 问题合并到同一个目标问题中,从而保持归并结果。对于预计不会再次出现的历史问题扩散,只使用合并。对于刚出现、希望提前处理且无需与现有问题合并的新模式,只使用归组规则。
工作流
步骤 1 — 确认重复项
按异常类型或消息搜索候选项:
posthog:query-error-tracking-issues-list
{
"searchQuery": "TypeError: Cannot read property",
"status": "active",
"limit": 50,
"orderBy": "occurrences",
"dateRange": { "date_from": "-30d" }
}
对每个候选项,获取一个采样异常事件,以比较堆栈、类型和消息:
posthog:query-error-tracking-issue-events
{
"issueId": "<candidate_issue_id>",
"limit": 1,
"include": ["exception", "stacktrace", "environment"]
}
对每个候选项执行一次。工具默认使用 onlyAppFrames: true,让最上方的应用内帧一目了然。如果两个候选项的最上方帧和异常类型相同,它们很可能是同一个错误——但合并前,请根据下面的完整检查清单进行核实。
它们是同一个错误吗?
只有在以下各项全部匹配时,才将两个问题视为重复问题:
$lib是同一个 SDK。浏览器/JS SDK 会将$lib记录为web(而不是posthog-js);服务端 SDK 使用posthog-python、posthog-node等。请使用read-data-schema(查询$exception的$lib对应的event_property_values)确认 确切值,不要凭猜测——值不正确会导致匹配结果为空,且不会提示错误。 即使异常类型相同,不同 SDK 的错误也几乎总是来自不同的代码路径。- 异常类型完全相同(
$exception_types)。 - 应用内堆栈的顶部调用帧指向同一个文件和同一个函数。行号和该函数内的细微偏移可以不同;顶部调用帧位于不同文件或函数,就意味着是不同的 Bug。
- 消息遵循相同的模板,差异仅限于易变数据——ID、哈希、时间戳、动态路径。如果差异是动词、对象或操作不同,那就是不同的 Bug。
$exception_handled一致(都已处理或都未处理)。被捕获的变体和未被捕获的变体属于不同的代码路径,最好分开处理。
如果以上任何一项不匹配,它们就不是重复问题——请分别调查
(investigating-error-issue)。
不要将以下情况归为一组
以下故障模式会破坏调试信号。即使用户称它们为“同一类 Bug”,也不要跨以下任何情况归组:
- 同一异常类型的前端和后端变体。 浏览器 Bundle 中的
TypeError和 Node 服务中的TypeError名称相同,消息中也经常有相同的词,但堆栈、运行时和修复方法都不同。 - 不同的 SDK / 平台。
web(浏览器/JS)与posthog-python、posthog-node属于不同的调用点。 - 类型相同,但堆栈顶部的文件或函数不同。 从
OrderService.cancel抛出的NullPointerException与从PaymentService.refund抛出的异常不是同一个 Bug,即使两者的消息都写着 “user was null”。 - 已捕获与未捕获。 两个问题仅
$exception_handled不同,通常表示某个代码路径会在一处吞掉错误,而在另一处让错误继续传播——将它们分开可以清楚地显示这一点。 - 概念相似、恰好有共同短语的 Bug。 “Cannot read property of undefined” 会出现在许多相互独立的 Bug 中。没有匹配的堆栈帧,仅凭消息相似并不足以判定为同一问题。
第 2 步 — 选择目标问题
选择应吸收其他问题的问题:
- 出现次数最多——保留占主导地位的问题,使仪表板数据保持连续
- 名称和描述最佳——如果用户已添加注释,优先选择该问题
- 最早的
first_seen——保留原始时间线
记下目标问题的 ID。其他候选项将成为要合并的 ids。
第 3 步 — 合并现有重复问题
posthog:error-tracking-issues-merge-create
{
"id": "<target_issue_id>",
"ids": ["<duplicate_id_1>", "<duplicate_id_2>", "..."]
}
合并具有破坏性(注释 destructive: true)——问题合并到目标后,源问题会从活动列表中移除。调用前请与用户确认目标。每次合并调用最多处理约 50 个源 ID,以便将故障影响范围限制在局部;如果涉及大量问题,请分批处理。
合并后的变更可能不会立即显示在问题列表中——调用后立即重新列出问题,短时间内仍可能看到源问题。如果后续的 error-tracking-issues-list 调用结果看起来没有变化,请等待几秒后重新查询,而不要再次发起合并。
如果合并后目标的元数据有误(例如某个重复问题的名称更好),请使用 error-tracking-issues-partial-update 修正目标的名称或描述,而不是重新合并。
步骤 4 — 判断是否需要创建分组规则
满足以下两个条件时,值得创建分组规则:
- 此模式持续产生新的指纹(自上次合并后,仍出现了新的重复项)
- 你可以用属性过滤器描述此模式,且不会意外涵盖无关错误
标准异常属性($exception_types、表示消息的 $exception_values、表示文件路径的 $exception_sources、表示函数名称的 $exception_functions)在捕获时都是数组。PostHog 的属性过滤器会对它们做特殊处理:每个过滤器都会匹配数组中的各个元素,因此所有标准运算符(exact、is_not、icontains、not_icontains、regex、not_regex)都可以直接使用值:
exact "TypeError",而不是 exact '["TypeError"]' 或 regex '"TypeError"'。
单数形式($exception_type、$exception_message)和 $exception_stack_trace_raw 只会出现在不到百分之一的事件中;以它们为条件创建的规则会悄无声息地永远无法匹配。
如果消息存在变化(例如,TypeError at /static/main.<hash>.js),可以在 $exception_values 上使用 regex 过滤器。如果已知文件中的行号存在变化,可以在 $exception_sources 上使用 icontains。$exception_handled 也是一个实用的细分维度——应区分已处理和未处理的异常,而不是混在一起。
以下情况应跳过分组规则:
- 重复项是历史遗留的(一次性回填,没有新的发生记录)——合并即可
- 无法编写足够精准且安全的过滤器——改为提高合并频率,之后再重新评估
步骤 5 — 创建分组规则
将步骤 1 中“相同错误”的检查清单转换为规则过滤条件。如果规则的匹配范围比检查清单更宽松,就会悄无声息地永久合并无关的 Bug——这种规则比合并更危险,因为它会针对之后的每个事件运行。至少应按 SDK 和异常类型进行限定,并添加第三个维度(通过 $exception_sources 指定文件路径,或通过 $exception_values 指定具体的消息短语)来锁定调用位置。
先确认 $lib 的值——浏览器/JS SDK 捕获的值是 $lib="web",而不是 posthog-js,因此按 posthog-js 过滤的规则会悄无声息地永远无法匹配。在将某个值写入规则之前,使用 read-data-schema(针对 $exception 查询 $lib 的 event_property_values)进行验证:
posthog:error-tracking-grouping-rules-create
{
"filters": {
"type": "AND",
"values": [
{
"type": "event",
"key": "$lib",
"operator": "exact",
"value": "web"
},
{
"type": "event",
"key": "$exception_types",
"operator": "exact",
"value": "TypeError"
},
{
"type": "event",
"key": "$exception_sources",
"operator": "icontains",
"value": "/static/checkout/"
},
{
"type": "event",
"key": "$exception_values",
"operator": "icontains",
"value": "Cannot read property"
}
]
},
"description": "Cleanup: collapse noisy checkout TypeError fingerprints (web)"
}
规则按顺序评估。先列出已有规则(posthog:error-tracking-grouping-rules-list)——如果某条规则已经部分覆盖了此模式,优先调整其过滤条件,而不是再叠加一条近似重复的规则。
可选字段 assignee 会自动将规则创建的问题分配给指定对象。除非用户明确希望在规则中固定负责人,否则应跳过此字段。
步骤 6 — 验证并整合
抽样检查已合并问题的近期事件,确认合并成功。留意规则的 custom-rule:<rule_id> 指纹是否开始匹配事件——首次匹配会创建一个新问题(或将事件路由到此前已关联该指纹的问题)。若要让事件归入历史目标问题,而不是分散到新的自定义规则问题中,请再执行一次合并,将自定义规则问题合并到目标问题中。
如果规则生效后仍持续出现新的(非规则)指纹,说明过滤条件过于严格——应放宽条件。
提示
- 用户经常会混淆分组规则和分配规则。分组规则决定事件归入 哪个 问题。分配规则决定由 谁 负责处理产生的问题。
- 不要仅凭“看起来相似”就合并问题,应先检查事件。发生在不同文件中的两个
TypeError是不同的错误。 - 堆栈帧是规范的分组信号——摄取阶段已经根据堆栈生成指纹,因此稳定的堆栈会自行完成分组。分组规则适用于自然指纹产生过多分组的情况(文件名易变、函数名称经过哈希处理、行号动态变化),此时需要覆盖默认规则。
- 禁用或收紧分组规则不会追溯性地取消现有事件的分组;未来的事件会正确路由,过去的事件仍留在原处。如果需要精确拆分已合并问题中的指纹,请使用
error-tracking-issues-split-create。 - 分组规则可在 UI 中通过 Project settings → Error tracking → Grouping rules 查看;用户询问规则在哪里时,请告知这一位置。
相关技能
suppressing-noisy-errors— 完全丢弃无价值的事件,而不是重新分组triaging-error-issues— 清理分组后,重新排列问题队列的优先级investigating-error-issue— 端到端检查已合并的问题