PostHog Web Analytics 支持工单分级排查实战指南:从队列枚举、诊断 Playbook 到修复产出
2026/9/11 7:18:10 网站建设 项目流程

PostHog Web Analytics 支持工单分级排查实战指南:从队列枚举、诊断 Playbook 到修复产出

【免费下载链接】posthog:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.项目地址: https://gitcode.com/GitHub_Trending/po/posthog

本指南以 PostHog 仓库内.agents/skills/triaging-web-analytics-support/技能文档为主体,完整呈现一套端到端的 Web Analytics 支持工单分级(Triage)方法论:先在数据仓库中枚举工单队列,再把工单归入六大诊断形态并执行对应 Playbook,最后产出有证据支撑的回复草稿与修复 PR。读完你将掌握 PostHog 内部支持团队处理“指标对不上”“流量下降”“Tracker 不加载”等高频问题的完整排查链路,并了解底层源码(HogQL 通道分类、会话入口属性聚合、ClickHouse 字典)如何支撑每一个诊断结论。

说明:该技能定位为内部工具(Internal-only)——它需要跨客户查询支持与用量数据,涉及客户名称、流量数字的内容严禁进入公开产物(PR、Issue、Commit)。文中所有 SQL 均针对 PostHog 内部 MCP 数据项目(US,project 2)编写。

一、分级工作的核心原则

技能文档开篇给出两条贯穿始终的准则:

  1. 先诊断、后写码:绝大多数被上报的“Bug”实际上是可解释的语义问题;而真正的 Bug 往往先出现在错误追踪(Error Tracking)或原始数据里,而不是代码里。
  2. 先定层、再下结论:任何症状都必须先判定它存在于哪一层——Capture(采集)→ Ingestion(摄入)→ 存储事件(Stored Events)→ 查询期分类(Query-time Classification)→ UI。例如原始count()的下降不可能由查询期的机器人排除逻辑引起;分类规则的变更也不可能改变已存储的计数。在提出修复方案前,必须先明确指出证据指向哪一层。

这两条原则分别对应了工单分类阶段的“层拆分”与“先查既有先例”两个横切规则。

二、第一步:枚举工单队列

工单存放在 PostHog 的 Conversations 产品中,可以通过 PostHog MCP 的execute-sql工具对内部项目的system.support_tickets表(project 2,US)进行查询;Zendesk 镜像则承载了完整评论历史。

该表在仓库中确有实现:posthog/hogql/database/schema/system.py 中以PostgresTable注册了support_tickets(见support_tickets: PostgresTable = PostgresTable(name="support_tickets", ...)),并配套tagsassignee等内部联结表,访问隔离通过ticket_id IN (SELECT id FROM system.support_tickets)形式的谓词作用域实现。技能文档提醒:查询system.*表前,务必先用system.information_schema.columns确认列结构。

2.1 拉取未处理的 Web Analytics 工单

references/ticket-queries.md 提供了开箱即用的队列扫描 SQL:

SELECT ticket_number, id, status, priority, channel_source, substring(last_message_text, 1, 400) AS last_msg, created_at, message_count FROM system.support_tickets WHERE status IN ('new', 'open', 'pending') AND created_at >= now() - INTERVAL 21 DAY AND (last_message_text ILIKE '%web analytics%' OR last_message_text ILIKE '%bounce%' OR last_message_text ILIKE '%utm%' OR last_message_text ILIKE '%pageview%' OR email_subject ILIKE '%web analytics%') ORDER BY created_at DESC

使用该查询需要注意几个已知陷阱:

  • last_message_text只有最新一条消息,且可能被截断(这里截取前 400 字符);message_count > 1意味着还有你没看过的历史对话。
  • 关键词过滤会漏掉措辞不同的工单——因此还要同时阅读#support-web-analyticsSlack 频道中镜像过来的 Zendesk 工单流;每条消息里的应用内工单链接携带 Conversations UUID。

2.2 通过 Zendesk 仓库镜像取完整评论历史

system.support_tickets没有消息表,完整评论历史在 Zendesk 镜像表的一个 JSON 数组列中。核心技巧是使用arrayJoin展开child_eventsJSON 数组,再用JSONExtractString提取正文:

SELECT created_at, body FROM ( SELECT created_at, JSONExtractString( arrayJoin(JSONExtractArrayRaw(assumeNotNull(toString(child_events)))), 'body') AS body FROM zendesk.ticket_events WHERE ticket_id = {zendesk_ticket_id} ) WHERE body != '' ORDER BY created_at ASC

三个关键细节:

  • assumeNotNull必不可少——arrayJoin不能直接作用于Nullable列;
  • 应在子查询之外应用LIMIT,否则arrayJoin会先行展开导致 LIMIT 失效;
  • MCP 展示会截断长单元格,应显式提取字段而不是直接倾倒原始 JSON。

2.3 将请求者邮箱解析到组织/团队(US 与 EU)

PostHog 内部数据项目区分 US 与 EU 区域,且EU 客户数据无法从 US MCP 项目查询。跨区域解析请求者邮箱时使用UNION ALL

SELECT 'us' AS region, u.email, u.current_team_id FROM postgres.posthog_user u WHERE u.email = '{email}' UNION ALL SELECT 'eu', u.email, u.current_team_id FROM eu_postgres_posthog_user u WHERE u.email = '{email}'

补充可用的辅助表:postgres.posthog_organizationdomain(仅已验证域名,小组织常为空)、按名称查询的postgres.posthog_organization、以及all_posthog_team。任一区域的单团队事件数据,则改用querying-production-databases-via-metabase技能(覆盖 prod-us 与 prod-eu 访问)。

三、第二步:按诊断形态分类并执行 Playbook

技能文档将 Web Analytics 工单归纳为六种诊断形态,每种形态都有明确的触发词与第一步动作:

形态触发词第一步动作
前端崩溃(Frontend crash)“everything crashes”、异常 ID、堆栈错误追踪查询;sourcemap 后的帧可定位文件。US 与 EU 项目都要查
两个数字对不上(Two numbers don't match)“两个不同的跳出率”、“insight X 与 tile Y 不一致”先查语义而非代码:事件级 vs 会话入口级作用域、“landing vs containing”、any-event vs entry-event 过滤器解释大多数情况
流量随时间下降(Count drop over time)“pageviews 下降”、“追踪丢失”层拆分:原始存储计数 vs 查询期排除。看$pageview$pageleave比值、UA 分段、SDK 版本固定;机器人形态流量消失很常见,不是 PostHog 的 Bug
Tracker 不加载 / 计数低于竞品“数字低于其他工具”、GTM、consent、广告拦截器用 Playwright 对客户线上站点做运行时加载审计:加载方式、首请求时机、黑名单模拟。见 references/loading-audit.md
广告平台集成错误“无法重新添加 source”、OAuth 报错、“没有转化”Source 重建路径、OAuth 失败模式(如 Microsoft AADSTS650052)、归因连接键(精确 campaign 名 + 归一化 source,兜底需两个 UTM 同时存在)
渠道类型误分类“显示为 Direct”、“渠道不对”查 channel_definitions.json + channel_type.py 中的决策树;未知 source + 被剥离的 referrer 会落到 Direct

两条横切规则:

  • 先定层再修复:明确证据指向 Capture → Ingestion → 存储事件 → 查询期分类 → UI 中的哪一层。
  • 先查先例再动手:检索既有 Issue/PR 与频道历史——技能文档明确提到自引用排除(self-referral exclusion)、AI 渠道类型、OAuth 错误提示等反复出现的诉求已有开放 Issue,其上下文会改变正确的回应方式。

3.1 Playbook:前端崩溃

  1. 拿到异常:工单通常带异常 ID 或压缩后的堆栈(chunk-XXXX.js帧)。
  2. 在内部项目的错误追踪中检索(query-error-tracking-issues-list配合searchQuery/webURL 过滤)。Issue 行的source字段给出 sourcemap 后的文件;用verbosity: stack查看事件可获得解析后的函数名。
  3. 阅读命中的代码路径。文档记录的 Web Analytics 高频崩溃类:kea-router 会 JSON 解析 query 参数——?someParam=123会变成数字、?p=a&p=b会变成数组;任何由searchParams喂入的持久化 reducer 都可能永久持有非字符串值,进而在每次 selector 重算(日期变更、过滤变更)时崩溃,包括那些只是connect了该 logic 的场景。
  4. 两端同时修复:在 router 边界强制类型转换,并且在读路径也做处理(仅修边界无法覆盖已持久化的坏值);补一个廉价的纯函数回归测试,断言非字符串输入不会抛异常。
  5. 区分 CI 噪音:门禁 Job(如 “X Tests Pass”)会在依赖 Job 被重复运行的 superseded 版本取消时失败——先读门禁日志再追查幻影测试失败;一个执行了零步骤就失败的 runner 属于基础设施问题,重跑即可。

3.2 Playbook:两个数字对不上

几乎总是作用域语义问题,主要有三种形态:

  • Landing vs Containing(落地 vs 包含):总览图块按“任意事件匹配过滤器”圈定会话;而按路径的跳出率/下钻行按会话入口值圈定。过滤器相同、分母不同,两者都正确。
  • 事件级过滤 vs 会话入口级下钻:按事件utm_campaign过滤会选中“包含任一匹配事件”的会话;而 UTM 下钻图块按会话$entry_utm_campaign分组。一个在会话中途才带上 UTM 的会话能匹配过滤器,却显示为 “(not set)”。
  • 算子漂移:URL 的containsvsequals、路径清洗开/关、同名的事件属性 vs 会话属性。

分辨率结论:精确定义两套口径,然后给客户对齐的一对——与会话级下钻比较时,就用会话入口属性过滤。只有当两个表面声称同一口径却仍然不一致时,才升级为 Bug。

源码佐证:该语义差异直接对应仓库中的会话入口属性聚合实现。posthog/hogql/database/schema/sessions_v2.py 中$entry_utm_campaign被定义为null_if_empty(arg_min_merge_field("initial_utm_campaign"))——即会话入口属性由initial_utm_campaign经过argMin合并聚合而来,这与事件级utm_campaign天然属于不同口径。相关查询构建器集中在 products/web_analytics/backend/hogql_queries/(overview 与 stats_table 两套 query builder,如 web_overview.py、stats_table.py)。

3.3 Playbook:流量随时间下降

决定性问题是:哪一层掉了?

  1. 拉原始日序列:通过querying-production-databases-via-metabase按天对事件做count()。如果原始计数下降,那么任何查询期变更(机器人分类、排除规则、物化)都不可能是原因——它们永远不会改变已存储的计数。
  2. 快速排除假线索的健全性检查:$lib+ SDK 版本分段(版本固定 = 无 SDK 回归)、重复 UUID 计数(去重)、按星期几配对比较(季节性)、按小时分布(区域/故障)。
  3. 机器人指纹:只有$pageview下降而$pageleave持平;损失集中在少数几个 UA 字符串,且周环比萎缩 10~50 倍;每个会话约 2 次 pageview 却没有 pageleave。这说明非人类流量停止执行 JS SDK——通常是客户侧边缘设施(WAF、bot-fight 模式、JS challenge)发生变化,或爬虫停止。他们的服务器日志仍会计数这些请求,这正是“我们的日志看起来没变”的原因。
  4. 回复框架:PostHog 存储的是它实际收到的内容;识别消失的流量段;请客户确认在具体日期他们的边缘设施改了什么;并说明新的更低水平更接近真实人类流量。

3.4 Playbook:Tracker 不加载 / 计数低于竞品

对客户线上页面执行运行时加载审计(见 references/loading-audit.md)。反复出现的高频结论:

  • 投递链比端点代理更重要:反向代理的api_host本身不可被拦截,但如果 SDK经由 GTM加载,拦截googletagmanager.com照样会杀死它。要么第一方脚本 + 第一方端点,否则都不算数。
  • Consent 延迟吃掉快速跳出:即使在无横幅区域,标签管理器与 consent 平台也是异步解析的;每个在该窗口前离开的访客都不会发送任何数据。需要测量首请求时机与竞品脚本的差距。
  • 对等比较:竞品工具在访问定义、机器人过滤、无 Cookie 计数上都有差异;先量化加载链差距,再谈口径差距。

3.5 Playbook:广告平台集成错误

  • 软删除的 Source 不会阻止重建:前缀检查会排除已删除行,所以出现 “Prefix already exists” 说明旧 Source 仍然活跃。
  • OAuth 重连失败是重建的头号障碍:Microsoft 的 AADSTS650052(缺少租户管理员同意 / service principal)会以裸invalid_clienttoast 形式出现。一定要问清楚确切的错误文本和它出现的位置(登录弹窗 vs 表单字段 vs 创建 toast)——每个位置对应不同的代码路径。
  • 账户选择器可能受项目管理员门控:成员看到权限错误,而管理员能看到账户。
  • Marketing Analytics 归因逻辑:转化事件优先用自己的 UTM 归因;否则回退到窗口内最近一次同时携带utm_campaignutm_source的 pageview,然后按大小写敏感的精确 campaign 名+ 归一化 source 做 LEFT JOIN。任何缺失都会落入 “organic”。如果付费转化为 0 而 organic 行很胖,说明归因连接键或“双 UTM 要求”失败,而不是目标(goals)配置失败。
  • 原生广告源只需同步其统计表即可获得花费指标;PostHog 事件只用于转化目标。

3.6 Playbook:渠道类型误分类

这是最能体现“查询期分类”层概念的一类:

  • 分类发生在查询期(HogQL),由 channel_definitions.json 驱动,通过channel_definition_dict这个 ClickHouse 字典参与计算;修改定义会自动重分类历史数据
  • 默认决策树以 Direct 兜底unknown source+$direct引用域最终映射到 Direct——因此在 referrer 被剥离的流量上,一个无法识别的utm_source就会显示为 Direct。正确修法是添加定义行,而不是改兜底逻辑——该兜底行为已被测试钉死为对垃圾 UTM 的刻意处理。
  • 定义变更需要 ClickHouse 迁移add_missing_channel_types只会 INSERT 缺失的 (domain, kind) 组合;类型变更需要重建(truncate → re-insert →SYSTEM RELOAD DICTIONARY)。同时要更新create_channel_definitions_file.py,否则下一次重新生成会回退 JSON。
  • **同源插页(bot challenge)**会破坏document.referrer却保留 query 字符串——自引用且 UTM 完好的流量是其签名。缓解手段:用before_send重写自引用、在客户自己的域名上加自定义渠道规则、缩小 challenge 范围。
3.6.1 源码级佐证:默认决策树与字典实现

默认分类逻辑完整实现在 posthog/hogql/database/schema/channel_type.py 的_initial_default_channel_rules_expr()中,其注释明确说明“该逻辑同时被官方文档引用,改动需同步两边”。核心是一段multiIf决策树,节选关键分支:

multiIf( match({campaign}, 'cross-network'), 'Cross Network', ({medium} IN ('cpc','cpm','cpv','cpa','ppc','retargeting') OR startsWith({medium},'paid') OR {has_gclid} OR {gad_source} IS NOT NULL), coalesce(lookupPaidSourceType({source}), ..., 'Paid Unknown'), ({referring_domain} = '$direct' AND {medium} IS NULL AND ({source} IS NULL OR {source} IN ('(direct)','direct','$direct')) AND NOT {has_fbclid}), 'Direct', coalesce(lookupOrganicSourceType({source}), ..., 'Unknown') )

要点:付费判定优先(gclid、fbclid、gad_source、paid medium 前缀),Direct 判定要求 referrer 为$direct且 source/medium 为空,最后兜底到 Referral/Unknown。自定义渠道规则(CustomChannelRule)支持 EXACT、IS_NOT、IS_SET、IS_NOT_SET、ICONTAINS、NOT_ICONTAINS、REGEX、NOT_REGEX 等算子,通过multiIf+coalesce叠加在默认规则之上(custom_rule_expr优先,未命中则落到内置规则)。

底层字典在 posthog/models/channel_type/sql.py 中构建:channel_definitionORDER BY (domain, kind)的 MergeTree 表,channel_definition_dict采用COMPLEX_KEY_HASHED()布局(复合主键 domain, kind),LIFETIME 为 3000~3600 秒,通过 DICT_READER 只读账号从表加载。数据来自 channel_definitions.json(仓库中约 1500+ 行,每条记录形如["domain/medium", "kind", "domain_type", "type_if_paid", "type_if_organic", "is_app"],例如["email", "medium", null, null, "Email", false]["andisearch.com", "source", "AI", null, "AI", false])。相关的两个迁移文件为 posthog/clickhouse/migrations/0069_add_channel_definitions.py 与 posthog/clickhouse/migrations/0073_add_missing_channel_types.py。

四、第三步:产出工件(Artifacts)

每个工单最终要产出三类工件:

  • 回复草稿:每一条论断都要落到file:line、查询结果或文档链接上。与其只解释客户“为什么错”,不如直接给出对齐的过滤器/属性——例如用会话$entry_utm_campaign替代事件utm_campaign
  • 修复 PR:每个修复使用一个 worktree + 分支,conventional commit 规范提交,用仓库模板创建 Draft PR。公开仓库安全红线:泛化描述 Bug,绝不包含客户名称、Zendesk 编号或客户流量数字;Slack/工单链接(需认证访问)可作为来源上下文。
  • 会话笔记:在.notes/维护一份持续更新的分级笔记,每个工单一个章节,且每个工单必须有明确的 “action left” 标记,便于人类接手队列。

五、第四步:验证工具

  • 运行时加载审计与流量模拟:references/loading-audit.md。
  • 生产查询侧检查(按团队事件序列、UA 分段、摄入告警):querying-production-databases-via-metabase技能覆盖 prod-us 与 prod-eu 访问。
  • 错误追踪:MCP 的query-error-tracking-issues-list/query-error-tracking-issue-events配合verbosity: stack可获得 sourcemap 后的帧。

5.1 运行时加载审计实操模式

Playwright(Chromium)双轮扫描 2~4 个代表性 URL(落地页、一个深层页面、一个带 UTM 参数的页面):

  1. 正常轮(Normal pass):真实用户 UA、US 时区/语言环境。记录对 tracker 域名的每个请求及相对导航开始的时间戳。加载完成后再等约 10 秒,评估页内状态:window.posthog__loadedconfig.api_hostconfig.token前缀、person_profiles)、标签管理器容器与dataLayer长度、consent 平台对象及其解析后的状态、竞品 tracker 全局变量与脚本标签。
  2. 黑名单轮(Blocklist pass):同上,但中断匹配 EasyPrivacy 风格域名列表的请求(googletagmanager.comgoogle-analytics.com、各分析厂商域名),对比哪些 tracker 存活。

结果解读要点:

  • 加载方式<head>内联片段 vs 标签管理器 vs 打包引入。代理了api_host但走 GTM 投递的,GTM 被拦截照样失效——投递链是最薄弱环节。
  • 首请求时差:tracker 相对导航开始的第一条网络活动毫秒数,与竞品的差距就是快速跳出的盲区。
  • Consent 地理门控:consent 平台在 GDPR 区域外常常根本不加载;那些区域里的延迟来自标签管理器启动,而不是横幅。

两个重要注意事项:

  • 主流 SDK 都会抑制自动化:posthog-js 与多数竞品能检测navigator.webdriver,不会从无头浏览器发送事件。审计中 0 条采集请求是预期现象,与真实用户无关——断言应基于脚本/运行时存在性与时机,而不是事件 POST。
  • ?utm_...测试参数的访问无害,但严禁向客户项目注入伪造的转化事件;另外数字型主机名在new URL()中会被解析为 IPv4,不要在合成输入上断言精确解析后的主机。

5.2 既有先例与演进方向

文档记录的既有资产:每次审计现场编写约 120 行的会话级审计脚本;一个更完整的 CLI(check-loadingnew-userreturning-user场景)曾存在于内部沙箱仓库,并有将其发布为 tools/traffic-sim/ 的开放计划(该目录当前已存在于仓库中,包含流量模拟相关脚本与文档)。此外,逐页对比表(加载方式、片段位置、config key/host、与基线匹配度)能让部分迁移状态一目了然——缺失 tracker 的页面会立刻显现。

六、附录:错误追踪交叉引用

前端崩溃类工单中,EU 客户报告的崩溃通常在 US 也有出现(PostHog 员工与 US 用户执行的是同一份代码)。在内部项目的错误追踪中用 MCP 工具检索即可;Issue 行的source字段指出 sourcemap 后的文件,verbosity: stack给出解析后的帧名——这样无需本地复现,就能从压缩的客户堆栈定位到file:line

参考资源索引

  • 技能主文档:.agents/skills/triaging-web-analytics-support/SKILL.md
  • 工单查询 SQL 集:.agents/skills/triaging-web-analytics-support/references/ticket-queries.md
  • 六大形态诊断 Playbook:.agents/skills/triaging-web-analytics-support/references/diagnostic-playbooks.md
  • 运行时加载审计指南:.agents/skills/triaging-web-analytics-support/references/loading-audit.md
  • 支持工单数据表定义:posthog/hogql/database/schema/system.py
  • 会话入口属性聚合:posthog/hogql/database/schema/sessions_v2.py
  • 渠道分类决策树:posthog/hogql/database/schema/channel_type.py
  • 渠道定义数据与字典:posthog/models/channel_type/channel_definitions.json、posthog/models/channel_type/sql.py
  • 渠道定义迁移:posthog/clickhouse/migrations/0069_add_channel_definitions.py、posthog/clickhouse/migrations/0073_add_missing_channel_types.py
  • Web Analytics 查询构建器:products/web_analytics/backend/hogql_queries/

【免费下载链接】posthog:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.项目地址: https://gitcode.com/GitHub_Trending/po/posthog

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询