好的 Span 名称应遵循“{谓语} {宾语}”结构,例如“处理付款”。避免在 Span 名称中包含唯一标识符以保持低基数。这种命名方式与 OpenTelemetry 语义约定一致,有助于构建强大而有效的可观测性策略。
作者: Juraci Paixão Kröhling (OllyGarden)
良好检测的一个最基本但经常被忽视的方面是命名。这篇文章是专门讨论 OpenTelemetry 中事物命名艺术和科学的系列文章中的第一篇。我们将从 span 开始,它是分布式追踪的构建块,并在最开始就告诉你最重要的收获:如何命名描述你独特业务逻辑的 span。
命名你的业务 Span
虽然 OpenTelemetry 的自动检测功能非常适合覆盖标准操作(例如传入的 HTTP 请求或数据库调用),但最有价值的见解通常来自你添加到自己的业务逻辑中的自定义 span。这些是你的应用程序域独有的操作。
对于这些自定义 span,我们建议采用一种借鉴基本语法的模式。简单明了的句子通常遵循 主语 -> 谓语 -> 宾语 结构。“主语”(执行工作的服务)已经是追踪上下文的一部分。我们可以使用该结构的其余部分作为我们的 span 名称:
{谓语} {宾语}
这种模式具有描述性,易于理解,并有助于保持较低的基数——我们稍后将讨论的一个关键概念。
- {谓语}:描述正在完成的工作的动词(例如:处理、发送、计算、渲染)。
- {宾语}:描述正在被操作的事物的名词(例如:付款、发票、购物车、广告)。
让我们看一些例子:
不好的命名 | 好的 Span 名称 | 为什么更好 |
---|---|---|
process_payment_for_user_jane_doe | process payment | 动词和宾语很清楚。用户 ID 属于属性。 |
sendinvoice#98765 | send invoice | 可聚合。你可以轻松找到发送所有发票的 P95 延迟。 |
render_ad_for_campaign_summer_sale | render ad | 具体活动是一个细节,而不是核心操作。将其放入属性中。 |
calculate_shipping_for_zip_90210 | calculate shipping | 操作是一致的。邮政编码是一个参数,而不是名称的一部分。 |
validation_failed | validate user_input | 专注于操作,而不是结果。结果属于 span 的状态。 |
通过遵守 {谓语} {宾语}
格式,你可以为你的业务操作创建一个清晰、一致的词汇表。这使你的追踪变得非常强大。产品经理可能会问,“处理付款需要多长时间?”,工程师可以立即筛选这些 span 并获得答案。
为什么这种模式有效
那么为什么 process payment
好,而 process*invoice*#98765
不好呢?原因是基数。
基数是指一条数据可以拥有的唯一值的数量。一个 span 名称应该具有低基数。如果在 span 名称中包含唯一标识符(如用户 ID 或发票号码),你将为每个操作创建一个唯一的名称。这会淹没你的可观测性后端,使分组和分析类似操作变得不可能,并且会显着增加成本。
{谓语} {宾语}
模式自然会产生低基数名称。唯一的、高基数的细节(invoice\_#98765, user_jane_doe
)属于 span 属性,我们将在以后的博客文章中介绍。
从语义约定中学习
这种 {谓语} {宾语}
方法不是随意的。它是一种最佳实践,反映了官方 OpenTelemetry 语义约定 (SemConv) 背后的原则。SemConv 为常见操作提供了一组标准化的名称,确保 HTTP 请求的 span 名称保持一致,无论使用哪种语言或框架。
当你仔细观察时,你会看到这种描述资源操作的相同模式在整个约定中回荡。通过对你的自定义 span 遵循它,你正在与整个 OpenTelemetry 生态系统的既定理念保持一致。
让我们看一些来自 SemConv 的例子。
HTTP span
对于服务器端 HTTP span,约定是 {method} {route}
。
- 示例:
GET /api/users/:ID
- 分析:这是一个作用于宾语 (
/api/users/:id
) 的动词 (GET
)。使用路由模板而不是实际路径 (/api/users/123
) 是保持低基数的完美示例。
数据库 span
数据库 span 通常命名为 {db.operation} {db.name}.{db.sql.table}
。
- 示例:
INSERT my_database.users
- 分析:这是一个作用于宾语 (
my_database
.users) 的动词 (INSERT
)。正在插入的特定值是高基数的,因此被正确地从名称中排除。
RPC span
对于远程过程调用,约定是 {rpc.service}/{rpc.method}
。
- 示例:
com.example.UserService/GetUser
- 分析:虽然格式不同,但原理是相同的。它描述了一个方法 (
GetUser
),这是一个动词,位于一个服务 (com.example.UserService
) 中,该服务是宾语或资源。
关键的收获是,通过使用 {谓语} {宾语}
,你正在使用与其余检测相同的语言。
培养健康的系统
命名 span 不是一件小事。它是构建强大而有效的可观测性策略的基础实践。通过为你的业务特定 span 采用清晰、一致的模式(如 {谓语} {宾语}
),你可以将遥测数据从混乱的混乱状态转变为精心照料的花园。
一个命名良好的 span 是对你未来和你的团队的礼物。它在压力大的中断期间提供清晰度,实现强大的性能分析,并最终帮助你构建更好、更可靠的软件。
在本系列的下一篇文章中,我们将深入研究下一个细节层次:span 属性。我们将探讨如何向你的 span 添加丰富的、高基数的上下文,这对于深度调试是必要的,而不会影响你的 span 名称的可聚合性。