文档概览
这是“正式的”OpenTracing 语义规范。 由于 OpenTracing 必须跨多种语言工作,因此本文档注意避免使用特定于语言的概念。 也就是说,自始至终都有一种理解,即所有语言都有一些“接口”的概念,它封装了一组相关的功能。
版本政策
OpenTracing 规范使用 Major.Minor 版本号,但没有 .Patch 组件。 当对规范进行向后不兼容的更改时,主要版本会增加。 次要版本会增加非破坏性更改,例如引入新的标准标签、日志字段或 SpanContext 引用类型。 (您可以在问题规范#2 中阅读有关此版本控制方案动机的更多信息)
OpenTracing 数据模型
OpenTracing 中的跟踪由它们的跨度隐式定义。 特别是,可以将 Trace 视为 Span 的有向无环图 (DAG),其中 Span 之间的边称为引用。
例如,以下是一个由 8 个 Span 组成的示例 Trace:
有时使用时间轴可视化跟踪更容易,如下图所示:
每个 Span 封装了以下状态:
- 操作名称
- 开始时间戳
- 完成时间戳
- 一组零个或多个键:值跨度标签。 键必须是字符串。 值可以是字符串、布尔值或数字类型。
- 一组零个或多个 Span 日志,每个日志本身都是一个键:值映射与时间戳配对。 键必须是字符串,但值可以是任何类型。 并非所有 OpenTracing 实现都必须支持每种值类型。
一个 SpanContext(见下文)
对零个或多个因果相关的 Span 的引用(通过这些相关 Span 的 SpanContext)
每个 SpanContext 封装了以下状态:
- 任何依赖 OpenTracing 实现的状态(例如,跟踪和跨度 ID)需要跨进程边界引用不同的跨度
- baggage item,它们只是跨越流程边界的键值对
Span 之间的引用
Span 可以引用零个或多个其他有因果关系的 SpanContext。 OpenTracing 目前定义了两种类型的引用:ChildOf 和 FollowsFrom。 这两种引用类型都专门为子 Span 和父 Span 之间的直接因果关系建模。 将来,OpenTracing 还可能支持具有非因果关系的 Span 的引用类型(例如,一起批处理的 Span、卡在同一队列中的 Span 等)。
ChildOf 引用:Span 可能是父 Span 的 ChildOf。 在 ChildOf 引用中,父 Span 在某些方面依赖于子 Span。 以下所有将构成 ChildOf 关系:
- 表示 RPC 服务器端的 Span 可能是表示该 RPC 客户端的 Span 的 ChildOf
- 代表 SQL 插入的 Span 可能是代表 ORM 保存方法的跨度的 ChildOf
- 许多执行并发(可能是分布式)工作的 Span 可能都是单独的 ChildOf 单个父 Span,它合并了在截止日期内返回的所有孩子的结果
对于作为父级的 ChildOf 的子级,这些都可能是有效的时序图。
FollowsFrom 引用:一些父级跨度不以任何方式依赖于他们的子级跨度的结果。 在这些情况下,我们只是说子 Span 在因果意义上跟随父 Span。 有许多不同的 FollowsFrom 参考子类别,在 OpenTracing 的未来版本中,可能会更正式地区分它们。
对于“FollowFrom”父级的子级,这些都可以是有效的时序图。
开放追踪 API
OpenTracing 规范中有三个关键且相互关联的类型:Tracer、Span 和 SpanContext。 下面,我们将介绍每种类型的行为; 粗略地说,每个行为都成为典型编程语言中的“方法”,尽管由于类型重载等原因,它实际上可能是一组相关的兄弟方法。
当我们讨论“可选”参数时,可以理解不同的语言有不同的方式来解释这些概念。 例如,在 Go 中,我们可能会使用“功能选项”习语,而在 Java 中,我们可能会使用构建器模式。
Tracer
Tracer 接口创建 Span 并了解如何跨进程边界注入(序列化)和提取(反序列化)它们。 形式上,它具有以下功能:
1、开始一个新的span
必需参数
- 一个操作名称,一个可读的字符串,它简明地表示 Span 完成的工作(例如,RPC 方法名称、函数名称或更大计算中的子任务或阶段的名称)。 操作名称应该是最通用的字符串,用于标识(统计上)有趣的 Span 实例类。 也就是说,“get_user”优于“get_user/314159”。
例如,以下是获取假设帐户信息的 Span 的潜在操作名称:
| Operation Name | Guidance |
|---|---|
| get | Too general |
| get_account/792 | Too specific |
| get_account | Good, and account_id=792 would make a nice Span tag |
可选参数
- 对相关 SpanContext 的零个或多个引用,如果可能,包括 ChildOf 和 FollowsFrom 引用类型的简写。
- 一个可选的显式开始时间戳; 如果省略,则默认使用当前的walltime
- 零个或多个标签
返回一个已经启动(但未完成)的 Span 实例
2、将 SpanContext 注入载体
必需参数
- 一个 SpanContext 实例
- 一个格式描述符(通常但不一定是字符串常量),它告诉 Tracer 实现如何在载体参数中对 SpanContext 进行编码
- 一种载体,其类型由格式决定。 Tracer 实现将根据格式对这个载体对象中的 SpanContext 进行编码。
3、从载体中提取 SpanContext
必需参数
- 一个格式描述符(通常但不一定是字符串常量),它告诉 Tracer 实现如何从载体参数解码 SpanContext
- 一种载体,其类型由格式决定。 Tracer 实现将根据格式从这个载体对象解码 SpanContext。
返回一个适合在通过 Tracer 启动新 Span 时用作参考的 SpanContext 实例。
注意:注入和提取所需的格式
注入和提取都依赖于一个可扩展的格式参数,该参数指示相关“载体”的类型以及 SpanContext 在该载体中的编码方式。 所有 Tracer 实现都必须支持以下所有格式。
- 文本MAP:任意字符串到字符串映射,键和值的字符集不受限制
- HTTP header:具有适用于 HTTP 标头(a la RFC 7230)的键和值的字符串到字符串映射。 在实践中,由于在外面处理 HTTP 标头的方式存在这种“多样性”,因此强烈建议 Tracer 实现使用有限的 HTTP 标头键空间并保守地转义值。
- 二进制:表示 SpanContext 的(单个)任意二进制 blob
Span
除了检索 Span 的 SpanContext 的方法外,在 Span 完成后,不能调用以下任何一个方法。
1、检索 Span SpanContext
应该没有参数。
返回给定 Span 的 SpanContext。 即使在 Span 完成后,也可以使用返回的值。
2、覆盖操作名称
必需参数
- 新的操作名称,它取代了启动 Span 时传入的任何内容
3、完成Span
可选参数
- Span 的明确完成时间戳; 如果省略,则隐式使用当前的walltime。、
除了检索 Span 的 SpanContext 的方法外,在 Span 实例完成后,不得在其上调用任何方法。
4、设置 Span 标签
必需参数
- 标签键,必须是字符串
- 标签值,必须是字符串、布尔值或数字类型
- 请注意,OpenTracing 项目记录了某些具有规定语义含义的“标准标签”。
5、记录结构化数据
必需参数
- 一个或多个键值对,其中键必须是字符串,值可以是任何类型。 某些 OpenTracing 实现可能比其他实现处理更多(或更多)某些日志值。
可选参数
- 显式时间戳。 如果指定,则它必须介于跨度的本地开始时间和结束时间之间。
- 请注意,OpenTracing 项目记录了某些具有规定语义含义的“标准日志键”。
6、设置包明细
Baggage items 是 key:value 字符串对,适用于给定的 Span、它的 SpanContext 和所有直接或可传递地引用本地 Span 的 Span。 也就是说,行李物品与跟踪本身一起在带内传播。
baggage item在全栈 OpenTracing 集成的情况下启用强大的功能(例如,来自移动应用程序的任意应用程序数据可以使其透明地一直进入存储系统的深处),并且伴随着一些强大的成本:使用这个 小心翼翼的功能。
请慎重使用此功能。 每个键和值都被复制到关联 Span 的每个本地和远程子节点中,这会增加大量网络和 CPU 开销。
必需参数
- The baggage key, 字符串
- The baggage value,字符串
7、获取包明细
必需参数
- key,字符串
返回baggage的值,或某些表示缺少此类值的指示。
SpanContext
SpanContext 更像是一个“概念”,而不是通用 OpenTracing 层的有用功能。 也就是说,它对 OpenTracing 实现至关重要,并且确实提供了自己的瘦 API。 大多数 OpenTracing 用户仅在启动新的 Span 时或在向/从某些传输协议注入/提取跟踪时通过引用与 SpanContext 交互。
在 OpenTracing 中,我们强制 SpanContext 实例是不可变的,以避免围绕 Span 完成和引用的复杂生命周期问题。
1、遍历所有baggages
这取决于语言以不同的方式建模,但从语义上讲,调用者应该能够在给定 SpanContext 实例的情况下一次有效地遍历所有baggage。
2、NoopTracer
所有 OpenTracing 语言 API 还必须提供某种 NoopTracer 实现,可用于标记控制 OpenTracing 或为测试注入无害的东西(等等)。 在某些情况下(例如,Java)NoopTracer 可能在它自己的打包工件中。
3、可选的 API 元素
某些语言还提供实用程序来围绕单个进程传递活动的 Span 和/或 SpanContext。 例如,opentracing-go 提供了帮助器来设置和获取 Go 的 context.Context 机制中的活动 Span。
