Wide Events 可统一指标、日志与追踪三类可观测性数据
All you need is Wide Events, not "Metrics, Logs and Traces"
Wide Events 可作为指标、日志和追踪的统一数据模型,作者认为 OpenTelemetry 对三类遥测数据的区分与复杂术语加剧了行业困惑。Meta 的 Scuba 以带字段和值的宽事件记录系统状态或调用事实,借助筛选、分组和采样率还原支持交互式事故排查。文中进一步把追踪、日志和指标视为 Wide Events 的特例,并讨论统一数据对关联分析和根因定位的价值。
文章用 Meta Scuba 的实践把追踪、日志和指标还原为统一事件模型,并展示筛选、分组与采样率还原的排查路径。
Charity Majors 的这段话大概是科技行业当前可观测性状况的最佳概括——彻底的、大规模的混乱。所有人都很困惑。什么是链路追踪?什么是跨度?一行日志是跨度吗?如果我有日志,还需要链路追踪吗?如果我有很棒的指标,为什么还需要链路追踪?这类问题没完没了。Charity 与可观测性系统 Honeycomb 的其他优秀同仁一起,在阐明这些问题方面做得非常出色。然而,根据我自己的经验,要解释 Charity 所说的“日志都是垃圾”究竟是什么意思仍然极其困难,更别说日志和链路追踪实质上是同一回事这一事实了。为什么所有人都如此困惑?
稍微有点尖刻,我要怪罪 Open Telemetry。是的,它正为现代可观测性技术栈提供动力,但我还是要为这场大范围的混乱怪罪它。不是因为它是个糟糕的方案——它很棒!而是因为它对 Open Telemetry 是什么、能做什么的呈现方式及整套解释思路,让可观测性显得棘手而复杂。
首先,Open Telemetry 从一开始就明确区分了链路追踪、指标和日志:
OpenTelemetry 是一组 API、SDK 和工具。使用它对软件进行插桩,并生成、收集和导出遥测数据(指标、日志和链路追踪),以帮助你分析软件的性能和行为。
然后,它进一步详细解释这 3 个概念。
这是 OpenTelemetry 网站中介绍链路追踪部分的截图。根据我与使用 OpenTelemetry 的人员交流的经验,这张示意图确实已成为与可观测性相关联的主要配图之一。对一些人来说,这就是可观测性本身。它还把链路追踪与其他一切区分开来。这显然不是日志,对吧?这看起来也不像指标,对吧?它是某种特殊的、可能有点神圣的、而且需要投入学习的东西。根据我的经验,一旦人们了解了链路追踪,他们只会在这张图以及跨度、根跨度、嵌套跨度等整套术语的语境下思考它们。OpenTelemetry 网站有一个术语表页面,包含 60 多个术语!这一切复杂得令人难以置信!
但更重要的是——这种对“日志、指标和链路追踪”的聚焦,是否体现了可观测性的真正力量?它确实覆盖了一些场景,这一点没错;但当涉及大规模分布式系统时,更重要的是具备“深入挖掘”数据的能力——对数据进行“切分加工”,构建并分析各种视图、关联数据、查找异常……而提供所有这些能力的系统确实存在。
我在 Meta 工作时,并不知道能够使用有史以来最好的可观测性系统是多么幸运的事。这个系统叫 Scuba,而且它以很大优势位列 top 1,是人们离开 Meta 后最想念的东西。
Scuba 的基本理念极其简单,人们无需借助术语表页面就能理解。它使用宽事件(Wide Event)。宽事件本质上只是一组带名称和值的字段,和 json 文档非常相似。如果你需要记录某些信息——无论是系统的当前状态,还是由 API 调用、后台作业或其他原因引发的事件——只需向 Scuba 写入一条宽事件即可。例如,如果某个系统投放广告,自然会希望记录广告展示(Ad Impressions)——也就是某个特定广告已被用户看到的事实。相应的宽事件可能是这样的:
{
"Timestamp": "1707951423",
"AdId": "542508c92f6f47c2916691d6e8551279”,
"UserCountry": "US",
"Placement": "mobile_feed",
"CampaingType": "direct_ads",
"UserOS": "Android",
"OSVersion": "14",
"AppVersion": "798de3c28b074df9a24a479ce98302b6",
...
}之所以称这类事件为“宽”,是因为鼓励把所有能想到的信息都塞进去。任何在特定数据背景下可能相关的内容——都放进去即可,日后或许会有用。这一做法为处理 未知的未知奠定了基础——某些目前想不到、日后可能在事故调查中显露出来的事物。
处理未知的未知,可以通过一个例子更好地加以说明。Scuba 有一个美观且直观的界面,便于探索和试用。它有一个可供选择指标进行查看的区域,也有用于筛选和分组的区域,Scuba 会绘制一张精美的时间序列图表。首次选择广告展示数据集时,只会绘制一张显示展示次数的图表:
如果用 SQL 精确表达这里所选的内容,那么大概如下:
SELECT COUNT(*) FROM AdImpressions
WHERE IsTest = False嗯,其实并不完全如此。Scuba 还有一个概念,叫作 原生采样。 当某个事件被写入 Scuba 时,还必须同时写入一个名为 samplingRate 的字段——表示这个特定事件所采用的采样率。Scuba 会利用这些信息,恰当地“放大”图表中显示的结果,因此无需在脑中完成这种放大计算。这是一个非常棒的概念,因为它支持动态采样——例如,某类展示事件可以比另一类事件采用更高的采样率,同时在 UI 中保留“真实”数值。因此,底层实际执行的查询是
SELECT SUM(samplingRate) FROM AdImpressions
WHERE IsTest = False请注意,整个“放大换算”过程都由 UI 透明完成,用户在查询时无需考虑这一点。
那么,假设某个警报触发了,并提示我们宝贵的广告展示图表看起来有些异常:
所有使用 Scuba 进行调查的人,第一反应都是“切块切丁”,也就是进行筛选或分组,看看我们能否了解点什么。我们不知道自己在找什么,但我们相信最终会发现它。所以,我们会按展示类型、用户所在国家或广告投放位置等条件进行分组,直到发现某些可疑之处。假设这次按 CampaignType 分组:
我们看到,某种名为 in_app_purchases 的广告系列类型(顺便说明一下,这个类型名称完全是我自己编的) 似乎与其他类型有所不同。我们其实不知道这是否意味着什么——我们不需要知道!——就能继续深挖。好了,现在我们可以只筛选这些广告系列,再继续按其他能想到的条件分组。例如,按 User OS 分组就很合理。
嗯,Android 似乎出了问题。iOS 没问题,这表明问题可能出在客户端——也许是某个应用版本坏了?
很奇怪。有些受到了影响,有些则没有。也许可以检查一下 OS Version?
哈!这是该操作系统的最新版本。看起来,对于此类广告系列,一些应用版本在这个操作系统版本上的表现不太理想。相关专门团队现在可能会据此进一步深入调查。
发生了什么?在完全不了解该系统的情况下,我们缩小了问题范围,并确定了由哪些团队牵头进一步调查。我们能不能事先知道,这种操作系统、操作系统版本、广告系列类型和应用版本的奇怪组合可能会引发问题,从而准备好一项专用指标?当然不能。这就是处理未知的未知的一个例子。我们刚刚把所有相关上下文都放进了宽事件,之后需要时再使用。Scuba 让探索变得简单,因为它运行速度快,而且 UI 非常美观、易用。另请注意,我们从未提及过基数。因为这无关紧要——任何字段都可以具有任意基数。Scuba 处理原始事件,不会做任何预聚合,因此基数不是问题。
有时,UI / 可视化方面没有得到足够重视,而可观测性系统提供的查询语言要么是专有语言(糟—糟—糟),要么是 SQL(稍好一点,但仍然很糟)。这种界面几乎让人无法开展类似调查。Scuba 有一个重要特点:所有字段——函数、筛选、分组等——都可以探索。这意味着,我们可以轻松查看有哪些类型的值可供选择。当某个字段的负责人没有偷懒时,他们甚至会为该字段附上详细说明以及相关链接等。这太棒了。我曾经亲自成功调查过许多故障事件,尽管当时既没有完全理解整个系统,也没有完全理解这个数据集中可用的数据。哎呀,在这些调查过程中,我仅仅通过摆弄 Scuba,就学到了很多关于这个系统的知识!这太令人惊叹了。这就是可观测性天堂。
现在想象一下,当我离开 Meta、了解到外部可观测性系统的现状时,自己有多么困惑和难以置信。
日志?追踪?指标?搞什么鬼?宽事件,有人在用吗?我能不能拜托别学术语表里的 60 个术语了,就直接……探索一番?
我花了相当长一段时间,才将基于 Scuba 的心智模型映射到 Open Telemetry 的心智模型。我意识到,Open Telemetry 的 Span 实际上就是宽事件。不过,我现在仍不太确定自己是否理解对了:

如果以 AdImpression 为例,这次广告展示其实并不是一项操作,而只是我们想记录的一项事实……公平地说,Open Telemetry 中也确实存在某种 Event 概念:

但如果沿着链接继续深挖,我们又会发现,Event 实际上属于追踪、指标或日志中的一种 🤷

不过话说回来,Span 是最接近宽事件的概念。问题在于——当 Open Telemetry 所建议的心智模型已经被学会后,要为这套心智模型大声倡导就极其困难。这确实令人沮丧,因为追踪、指标和日志实际上都只是宽事件的特殊情况:
追踪和 Span:这些只是包含 SpanId、TraceId 和 ParentSpanId 字段的宽事件。因此,我们可以筛选出具有指定 TraceId 的所有 Span,根据 SpanId → ParentSpanId 关系对它们进行拓扑排序,并绘制出大家喜爱的分布式追踪视图。
日志:说实话,我真的很困惑 Open Telemetry 所说的日志究竟是什么意思。看起来似乎有很多东西,其中之一是结构化日志,这基本上就是宽事件。很好!然而,问题在于,“日志”是一个定义相当明确的概念,而人们通常指的是由那些
logger.info(…)调用产生的内容。无论如何,当然,日志很容易映射为宽事件。最简单的情况下,我们可以只获取日志消息,将其放入“log_message”字段,再添加一堆元数据,就大功告成。更复杂的情况下,我们可以尝试通过移除看起来像 ID 的标记,从日志消息中自动提取模板,并计算这个模板的哈希值。例如,这样便可以通过按哈希值分组,快速找出最常见的错误。Meta 就有这样一套系统,而且相当酷。指标:指标同样很容易映射。我们只需每隔一段时间发出一个宽事件,其中包含系统状态(例如 cpu、各类计数器等系统指标,……)。顺便说一句,Prometheus 的抓取方式正是这么做的——定期获取系统快照。不过,与 prometheus 不同的是,采用宽事件方式时,我们不必担心基数问题。
但宽事件所能提供的远不止这些“3 大支柱”。前面提到的调试过程已经是一个无法真正由追踪、日志和指标覆盖的案例——至少并非自然涵盖。还可能有其他用例——例如,持续性能分析数据可以轻松表示为宽事件,并通过查询构建 火焰图。无需为此建立单独的系统——一个处理宽事件的单一系统就能完成所有工作。想象一下,当所有内容都集中在一处并一同存储时,交叉关联 & 根因分析会有多大的潜力。尤其是在如今这个基于 AI 的工具不断涌现、且非常擅长在数据中发现关联的时代。
我不知道……我只是想表达我对如此混乱以及这种对“3 大支柱”的关注感到的不满。
我真希望可观测性厂商能坚决反对这种混乱,并提供一种简单自然的方式来与系统交互。 Honeycomb似乎正在这么做,其他一些系统(如 Axiom)也一样。这太好了!也希望其他厂商能跟上。
来源:Hacker News · isburmistrov.substack.com