Tracing SDK
Status: Stable, 除非另有说明
Tracer Provider
Tracer Creation
Tracer 实例只能通过 TracerProvider 创建(参见 API)。
TracerProvider 必须实现 Get a Tracer API。
用户提供的输入必须用于创建 InstrumentationScope 实例,该实例存储在创建的 Tracer 上。
状态: 开发中 - TracerProvider 必须使用配置的 TracerConfigurator 计算相关的 TracerConfig,并创建一个行为符合该 TracerConfig 的 Tracer。
配置
配置(即 SpanProcessors、IdGenerator、SpanLimits、Sampler 和(开发中)TracerConfigurator)必须由 TracerProvider 拥有。配置可以在创建 TracerProvider 时应用,如果合适的话。
TracerProvider MAY 提供更新配置的方法。如果配置被更新(例如,添加 SpanProcessor),则更新的配置也必须适用于所有已返回的 Tracers(即,Tracer 是在配置更改之前还是之后从 TracerProvider 获取的,这无关紧要)。注意:在实现上,这可能意味着 Tracer 实例具有对其 TracerProvider 的引用,并且仅通过此引用访问配置。
TracerConfigurator
状态: 开发中
TracerConfigurator 是一个计算 TracerConfig 以用于 Tracer 的函数。
该函数必须接受以下参数
tracer_scope:Tracer的InstrumentationScope。
该函数必须返回相关的 TracerConfig,或某种指示应使用 默认 TracerConfig 的信号。此信号可以是 nil、null、空,或默认 TracerConfig 的实例,具体取决于语言中的习惯用法。
此函数在 Tracer 首次创建时被调用,并在 TracerProvider 的 TracerConfigurator 更新时(如果支持更新)为每个已有的 Tracer 被调用。因此,它能够快速返回很重要。
TracerConfigurator 被建模为一个函数,以最大化灵活性。然而,实现 MAY 提供快捷方式或辅助函数来适应常见用例。
- 通过名称选择一个或多个 Tracer,支持精确匹配或模式匹配。
- 禁用一个或多个特定的 Tracer。
- 禁用所有 Tracer,并有选择地启用一个或多个特定的 Tracer。
Shutdown
此方法提供了一种方式,让 provider 进行任何必要的清理。
每个 TracerProvider 实例的 Shutdown 必须只调用一次。调用 Shutdown 后,不允许尝试获取 Tracer。SDK 应尽可能为这些调用返回一个有效的 no-op Tracer。
Shutdown 应提供一种方式,让调用者知道它是否成功、失败或超时。
Shutdown 应在某个超时时间内完成或中止。Shutdown 可以实现为阻塞 API 或异步 API,通过回调或事件通知调用者。OpenTelemetry 客户端作者可以决定是否要使 shutdown 超时可配置。
Shutdown 必须至少通过调用所有内部处理器中的 Shutdown 来实现。
ForceFlush
此方法提供了一种让提供者立即导出所有尚未导出的 span 的方式,用于所有内部处理器。
ForceFlush 应提供一种方式,让调用者知道它是否成功、失败或超时。
ForceFlush 应在某个超时时间内完成或中止。ForceFlush 可以实现为阻塞 API 或异步 API,通过回调或事件通知调用者。OpenTelemetry 客户端作者可以决定是否要使 flush 超时可配置。
ForceFlush 必须调用所有已注册 SpanProcessors 的 ForceFlush。
Tracer
状态: 开发中 - Tracer 必须根据 Tracer creation 期间计算的 TracerConfig 进行行为。如果 TracerProvider 支持更新 TracerConfigurator,则在更新后,Tracer 必须更新以根据新的 TracerConfig 进行行为。
TracerConfig
状态: 开发中
TracerConfig 定义了 Tracer 行为的各种可配置方面。它包含以下参数:
disabled:一个布尔值,指示 Tracer 是否已启用。如果未显式设置,
disabled参数应默认为false(即Tracer默认启用)。如果
Tracer被禁用,它必须表现得等同于一个 No-op Tracer。disabled的值必须用于解析Tracer是否 Enabled。如果disabled为true,Enabled返回false。如果disabled为false,Enabled返回true。实现不必确保对
disabled的更改对Enabled的调用者立即可见。然而,更改必须最终可见。
Enabled
状态: 开发中
Enabled 在以下任一情况下必须返回 false:
- 没有注册的
SpanProcessors; Tracer被禁用(TracerConfig.disabled为true)。
否则,应返回 true。为了支持其他优化和功能,可以返回 false。
Additional Span Interfaces
Span 接口的 API 级别定义仅定义了 span 的写访问权限。这很好,因为 instrumentations 和应用程序不应使用 span 中存储的数据进行应用程序逻辑。然而,SDK 最终需要在某些位置读取回数据。因此,SDK 规范定义了 Span 类参数的可能要求集。
Readable span:接收此参数的函数必须能够访问添加到 span 的所有信息,如 Span 的 API 规范中所列。注意:下面,有几个特定属性被单独列出以供澄清,但完整的必需属性列表,Span API 规范为权威。
接收此参数的函数必须能够访问与 span 关联的
InstrumentationScope[自 1.10.0 起] 和Resource信息(隐式)。为了向后兼容,它还必须能够访问与InstrumentationScope具有相同名称和版本值的InstrumentationLibrary[自 1.10.0 起已弃用]。接收此参数的函数必须能够可靠地确定 Span 是否已结束(某些语言可能通过将结束时间戳实现为
null来实现这一点,其他语言可能有显式的hasEnded布尔值)。由于收集限制而丢弃的属性、事件和链接的数量必须可供导出器报告,如 导出器规范中所述。
作为 API 规范中定义的权威 span 属性集的例外,实现 MAY 选择不公开(和存储)Span 的完整父 Context,但它们必须至少公开完整的父 SpanContext。
接收此参数的函数可能无法修改 Span。
注意:这通常会使用新的接口或(不可变的)值类型来实现。在某些语言中,SpanProcessors 可能具有与导出器不同的可读 span 类型(例如,
SpanData类型可能包含不可变的快照,而ReadableSpan接口可能直接从Span接口操作的同一底层数据结构中读取信息)。Read/write span:接收此参数的函数必须能够访问 API 级别定义的 span 接口中定义的完整 span API,并且还必须能够检索添加到 span 的所有信息(如同 *readable span*)。
被调用方必须能够以某种方式获得与 span creation API 返回给用户的相同的
Span实例和类型(例如,Span可以是传递给 such a function 的参数之一,或者可以提供一个 getter)。
采样
采样是一种机制,通过减少收集并发送到后端的跟踪样本数量来控制 OpenTelemetry 引入的噪声和开销。
采样可能在跟踪收集的不同阶段实现。最早的采样可能发生在跟踪实际创建之前,而最新的采样可能发生在 Collector 上,这在进程之外。
OpenTelemetry API 具有两个负责数据收集的属性:
Span的IsRecording字段。如果为false,则当前Span会丢弃所有跟踪数据(属性、事件、状态等)。用户可以使用此属性来确定是否可以避免收集昂贵的跟踪数据。Span Processor 必须只接收此字段设置为true的 span。但是,Span Exporter 不应接收它们,除非也设置了Sampled标志。SpanContext上的TraceFlags中的Sampled标志。此标志通过SpanContext传播给子 Span。更多详细信息请参阅 W3C Trace Context specification。此标志表示Span已被sampled并且将被导出。Span Exporters 必须接收Sampled标志设置为 true 的 span,并且不应接收未设置的 span。
标志组合 SampledFlag == false 和 IsRecording == true 意味着当前 Span 确实记录了信息,但子 Span 很可能不会。
标志组合 SampledFlag == true 和 IsRecording == false 可能导致分布式跟踪中的间隙,因此 OpenTelemetry SDK 必须不允许此组合。
Recording Sampled reaction table
下表总结了 IsRecording 和 SampledFlag 的每种组合的预期行为。
IsRecording | Sampled Flag | Span Processor 接收 Span? | Span Exporter 接收 Span? |
|---|---|---|---|
| true | true | true | true |
| true | false | true | false |
| false | true | 不允许 | 不允许 |
| false | false | false | false |
SDK 定义了 Sampler 接口以及一组 内置采样器,并将一个 Sampler 与每个 [TracerProvider] 相关联。
SDK Span creation
当被要求创建 Span 时,SDK 必须按以下顺序执行操作:
- 如果存在有效的父跟踪 ID,则使用它。否则,生成一个新的跟踪 ID(注意:这必须在调用
ShouldSample之前完成,因为它需要一个有效的跟踪 ID 作为输入)。 - 查询
Sampler的ShouldSample方法。 - 为
Span生成一个新的 span ID,独立于采样决策。这样做是为了让其他组件(如日志或异常处理)即使在Span是非记录实例的情况下也能依赖于唯一的 span ID。 - 根据
ShouldSample返回的决策创建 span:请参阅 belowShouldSample的返回值说明,了解如何设置 Span 上的IsRecording和Sampled,以及 上表关于是否将Span传递给SpanProcessors。非记录 span MAY 使用与创建没有已安装 SDK 的Span相同的机制来实现,或如 wrapping a SpanContext in a Span 中所述。
Span flags
OTLP 中 Span 和 Span Link 的表示包含一个声明为 Span Flags 的 32 位字段。
Span Flags 字段的位 0-7(8 个最低有效位)保留给 W3C Trace Context Level 2 候选推荐中指定的 Trace Context 标志的 8 位。 查看已识别标志列表。
Sampler
Sampler 接口允许用户创建自定义采样器,这些采样器将根据在 Span 创建之前通常可用的信息返回采样 SamplingResult。
ShouldSample
返回要创建的 Span 的采样决策。
Required arguments
Context,包含父Span。Span 的 SpanContext 可能无效,以表示根 span。- 要创建的
Span的TraceId。如果父SpanContext包含一个有效的TraceId,它们必须始终匹配。 - 要创建的
Span的名称。 - 要创建的
Span的SpanKind。 - 要创建的
Span的初始属性集。 - 将与要创建的
Span关联的链接集合。通常对批量操作有用,请参阅 Links Between Spans。
注意:实现 MAY 将所有参数或几个参数“捆绑”到一个对象中。
Return value
它产生一个名为 SamplingResult 的输出,其中包含:
- 一个采样
Decision。以下枚举值之一:DROP-IsRecording将为false,Span将不被记录,所有事件和属性都将被丢弃。RECORD_ONLY-IsRecording将为true,但Sampled标志不得设置为。RECORD_AND_SAMPLE-IsRecording将为true,并且Sampled标志必须设置为。
- 一组 span 属性,它们也将被添加到
Span。返回的对象必须是不可变的(多次调用可以返回不同的不可变对象)。 - 一个
Tracestate,它将通过新的SpanContext与Span关联。如果采样器在此处返回空的Tracestate,Tracestate将被清除,因此采样器通常应返回传入的Tracestate,如果它们无意更改它。
GetDescription
返回采样器的名称或带有配置的简短描述。这可能会显示在调试页面或日志中。例如:"TraceIdRatioBased{0.000100}"。
Description MAY 随时间变化,例如,如果采样器支持动态配置或以其他方式调整其参数。调用者不应缓存返回的值。
Built-in samplers
OpenTelemetry 支持多种内置采样器可供选择。默认采样器是 ParentBased(root=AlwaysOn)。
AlwaysOn
- 始终返回
RECORD_AND_SAMPLE。 - Description 必须是
AlwaysOnSampler。
AlwaysOff
- 始终返回
DROP。 - Description 必须是
AlwaysOffSampler。
TraceIdRatioBased
状态: 稳定
TraceIdRatioBased 采样器已弃用,推荐使用可组合的 ProbabilitySampler。此组件正在 逐步淘汰,以解决 1.0 trace 规范中的“TODO”。OpenTelemetry SDK 实现者在至少 2027 年 1 月 1 日之前不得删除或修改原始 TraceIdRatioBased 采样器的行为。届时,鼓励 SDK 实现者默默地将 TraceIdRatioBased 配置替换为同等配置的 ProbabilitySampler。
TraceIdRatioBased必须忽略父SampledFlag。为了尊重父SampledFlag,TraceIdRatioBased应作为下面指定的ParentBased采样器的代理。- Description 必须返回形式为
"TraceIdRatioBased{RATIO}"的字符串,其中RATIO被替换为 Sampler 实例的跟踪采样率,表示为十进制数。数字的精度应遵循实现语言的标准,并且应足够高,以便识别 Samplers 何时具有不同的比率。例如,如果 TraceIdRatioBased Sampler 的采样率为 1:10,000,则它可以返回"TraceIdRatioBased{0.000100}"作为其描述。
Requirements for TraceIdRatioBased sampler algorithm
状态: 稳定
- 采样算法必须是确定性的。由给定的
TraceId标识的跟踪,无论语言、时间等如何,都是被采样或不被采样。为了实现这一点,实现必须使用TraceId的确定性哈希来计算采样决策。通过确保这一点,在任何子Span上运行采样器都将产生相同的决策。 - 具有给定采样概率的
TraceIdRatioBased采样器也必须采样任何具有较低采样概率的TraceIdRatioBased采样器将要采样的所有跟踪。当后端系统希望以比前端系统更高的采样概率运行时,这一点很重要,这样前端的所有跟踪仍将被采样,而额外的跟踪仅在后端被采样。
Compatibility warnings for TraceIdRatioBased sampler
状态: 开发中
警告:确切的算法从未被指定。此采样器未被定义为与任何其他 SDK 兼容,因此被认为不稳定。只有配置和创建 API 是稳定的。建议仅将此采样器算法用于根 span(与 ParentBased 结合使用),因为不同语言的 SDK 或同一语言 SDK 的不同版本可能会为相同的输入产生不一致的结果。
当此采样器观察到非空父 span 上下文时,即当它不作为根采样器使用时,SDK 应发出警告,例如
WARNING: The TraceIdRatioBased sampler is operating as a child sampler;
the behavior is subject to change. Please upgrade this SDK configuration
to use ProbabilitySampler.
在这种情况下,此采样器还可以通过检查 Tracestate 中的 OpenTelemetry tracestate(ot=...)以及 th 或 rv 子键来检测 ProbabilitySampler 的使用,在这种情况下,警告 MAY 更直接。
WARNING: The TraceIdRatioBased sampler is operating as a child sampler
and a parent is using ProbabilitySampler. Please upgrade this SDK configuration
to use ProbabilitySampler.
ProbabilitySampler
状态: 开发中
ProbabilitySampler 使用 W3C Trace Context Level 2 候选推荐中指定的随机性特征来实现简单的、基于比率的概率采样。OpenTelemetry 遵循 W3C Trace Context Level 2,该规范指定了 56 位随机性,指定如何使用 56 位随机性做出一致的概率采样决策。
ProbabilitySampler 采样器必须忽略父 SampledFlag。要尊重父 SampledFlag,请参阅下面指定的 ParentBased 采样器。
请注意,这是一个非可组合形式的概率采样器。ProbabilitySampler 直接实现 SDK 的 Sampler API,而 ComposableProbability 是用于与 CompositeSampler 结合使用的可组合形式。
ProbabilitySampler sampler configuration
ProbabilitySampler 采样器通常使用 32 位或 64 位浮点数来配置,以表示采样率。最小有效采样率为 2^-56,最大有效采样率为 1.0。从输入的采样率,将计算一个拒绝阈值;有关将采样率转换为具有可变精度的阈值的详细信息,请参阅 consistent-probability sampler requirements。
ProbabilitySampler sampler algorithm
给定一个配置了采样阈值 T 的 Sampler 和带有随机值 R 的 Context(通常是跟踪 ID 的最右边的 7 个字节),当调用 ShouldSample() 时,它使用表达式 R >= T 来决定是返回 RECORD_AND_SAMPLE 还是 DROP。
- 当(R >= T)时,即(R >= T),返回
RECORD_AND_SAMPLE,否则,返回DROP。 - 当(R >= T)时,OpenTelemetry TraceState 应根据 OpenTelemetry TraceState
th子键的规定,通过包含键值对th:T来修改,其中 T 为拒绝阈值。
Compatibility warnings for ProbabilitySampler
当 ProbabilitySampler Sampler 基于 TraceID 随机性为非根 Span 做出决策时,TraceID 可能实际上是由一个旧的 SDK 生成的,该 SDK 对此规范不知情。Trace random 标志允许我们区分这两种情况。此标志传播信息,让 ProbabilitySampler Samplers 确认 TraceIDs 是随机的,但这需要处理了上下文的每个 Trace SDK 都支持 W3C Trace Context Level 2。
当 ProbabilitySampler Sampler 在 TraceID 随机性(未设置 Trace random 标志时)为非根 Span 做出决策时,SDK 应在其日志中发出带有兼容性警告的声明。此兼容性警告示例为:
WARNING: The ProbabilitySampler sampler is presuming TraceIDs are random
and expects the Trace random flag to be set in confirmation. Please
upgrade your caller(s) to use W3C Trace Context Level 2.
ParentBased
- 这是一个采样器装饰器。
ParentBased有助于区分以下情况:- 无父项(根 span)。
- 远程父项(
SpanContext.IsRemote() == true)且SampledFlag已设置 - 远程父项(
SpanContext.IsRemote() == true)且SampledFlag未设置 - 本地父项(
SpanContext.IsRemote() == false)且SampledFlag已设置 - 本地父项(
SpanContext.IsRemote() == false)且SampledFlag未设置
必需参数
root(Sampler)- 为无父项(根 span)的 span 调用采样器
Optional parameters
remoteParentSampled(Sampler)(默认值:AlwaysOn)remoteParentNotSampled(Sampler)(默认值:AlwaysOff)localParentSampled(Sampler)(默认值:AlwaysOn)localParentNotSampled(Sampler)(默认值:AlwaysOff)
| Parent | parent.isRemote() | parent.IsSampled() | Invoke sampler |
|---|---|---|---|
| absent | 不适用 | 不适用 | root() |
| present | true | true | remoteParentSampled() |
| present | true | false | remoteParentNotSampled() |
| present | false | true | localParentSampled() |
| present | false | false | localParentNotSampled() |
JaegerRemoteSampler
Jaeger remote sampler 允许远程控制 SDK 的采样配置。采样配置会定期从后端加载(请参阅 Remote Sampling API),操作员可以通过配置文件进行管理,甚至可以自动计算(请参阅 Adaptive Sampling)。远程采样器检索的采样配置可以指示它为整个服务使用一种采样方法(例如,TraceIdRatioBased),或者为不同的端点(span 名称)使用不同的方法,例如,对 /product 端点进行 10% 的采样,对 /admin 端点进行 100% 的采样,而永不采样 /metrics 端点。
完整的 Protobuf 定义可以在 jaegertracing/jaeger-idl/api_v2/sampling.proto 中找到。
创建采样器时应提供以下配置属性:
- endpoint - 实现 Remote Sampling API 的服务的地址,例如 Jaeger Collector 或 OpenTelemetry Collector。
- polling interval - 从远程获取配置的轮询间隔
- initial sampler - 在首次获取配置之前使用的初始采样器
AlwaysRecord
状态: 开发中
AlwaysRecord 是一个采样器装饰器,它确保每个 span 都传递给 SpanProcessor,即使是那些通常会被丢弃的 span。它通过将包装的采样器返回的 DROP 决策转换为 RECORD_ONLY 决策来实现这一点,从而允许处理器看到所有 span 而无需将它们发送到导出器。这通常用于实现准确的 span 到 metrics 的处理。
根据包装的根采样器的决策,AlwaysRecord 必须执行以下操作:
| Root sampler decision | AlwaysRecord decision |
|---|---|
DROP | RECORD_ONLY |
RECORD_ONLY | RECORD_ONLY |
RECORD_AND_SAMPLE | RECORD_AND_SAMPLE |
必需参数
root(Sampler)- 被包装的采样器;它提供 AlwaysRecord 修改的原始采样/丢弃决策。
CompositeSampler
状态: 开发中
CompositeSampler 是一个装饰器,它实现标准的 Sampler 接口,但使用采样器的组合来做出其决策。
CompositeSampler 接收 ComposableSampler 作为输入,并将采样决策委托给该接口。有关更多详细信息,请参阅 Probability Sampling in TraceState。
ComposableSampler
ComposableSampler 是一个专门的接口,它扩展了标准的 Sampler 功能。它通过定义一个名为 GetSamplingIntent 的新方法,引入了一种可组合的采样方法,该方法允许多个 sampler 协同工作以做出采样决策。
GetSamplingIntent
返回一个 SamplingIntent 结构,该结构指示 sampler 对采样 Span 的偏好,而无需实际做出最终决定。
Required arguments
- 包含所有原始 Sampler API 参数
- 父上下文、阈值、传入的 trace 状态和 trace 标志信息 MAY 被预先计算,以便 ComposableSamplers 不会反复探测 Context 以获取此信息。
注意:ComposableSamplers MUST NOT 修改传递给委托的 GetSamplingIntent 方法的参数,因为它们被视为只读状态。
Return value
该方法返回一个 SamplingIntent 结构,其中包含以下元素
threshold- 采样阈值。较低的阈值会增加采样的可能性。threshold_reliable- 一个布尔值,指示阈值是否可以可靠地用于 Span-to-Metrics 估算。attributes_provider- 一个可选的属性提供程序,如果 Span 被采样,则将其添加到 Span 中。trace_state_provider- 一个可选的修改后的 TraceState 提供程序。
请注意,trace_state_provider 可能是复杂性的重要来源。ComposableSamplers MUST NOT 修改 OpenTelemetry TraceState(即 TraceState 的 ot 子键)。调用者 CompositeSampler SHOULD 更新传出 TraceState 的阈值(除非 !threshold_reliable),并且显式随机值 MUST 不得修改。
内置 ComposableSamplers
ComposableAlwaysOn
- 始终返回一个
SamplingIntent,其阈值设置为采样所有 Span(阈值 = 0) - 将
threshold_reliable设置为true - 不添加任何属性
ComposableAlwaysOff
- 始终返回一个
SamplingIntent,不带阈值,表示所有 Span 都应被丢弃 - 将
threshold_reliable设置为false - 不添加任何属性
ComposableProbability
- 返回一个
SamplingIntent,其阈值由配置的采样率确定 - 将
threshold_reliable设置为true - 不添加任何属性
必需参数
ratio- 一个介于2^-56和 1.0(含)之间的值,表示所需的采样概率。
值为 0 的 ratio 被视为非概率性。对于零情况, SHOULD 返回一个 ComposableAlwaysOff 实例。
请注意,顶层 ProbabilitySampler 可以使用 Composite(ComposableProbability(ratio)) 配置来实现。
ComposableParentThreshold
- 对于没有父上下文的 Span,委托给根采样器
- 对于具有父上下文的 Span,返回一个
SamplingIntent,该 Intent 传播父项的采样决策 - 返回父项的阈值(如果可用);否则,如果父项的 *sampled* 标志已设置,则返回 threshold=0;否则,如果父项的 *sampled* 标志未设置,则不返回阈值。
- 将
threshold_reliable设置为与父项的可靠性匹配,如果父项具有阈值,则为 true。 - 不添加任何属性
必需参数
root- 用于对没有父上下文的 Span 进行采样的委托。
ComposableRuleBased
- 根据谓词评估一系列规则,并返回第一个匹配采样器的
SamplingIntent - 如果没有规则匹配,则返回一个非采样 Intent
必需参数
rules- 一对 (Predicate, ComposableSampler) 列表,其中 Predicate 是一个函数,用于评估规则是否适用
ComposableAnnotating
- 将采样决策委托给另一个采样器,但为已采样的 Span 添加属性
- 返回一个
SamplingIntent,它结合了委托程序的阈值和附加属性
必需参数
attributes- 要添加到已采样 Span 的属性delegate- 做出实际采样决策的底层采样器
配置示例
创建组合采样器配置的示例
// Create a rule-based sampler for root spans
rootSampler = ComposableRuleBased([
(isHealthCheck, ComposableAlwaysOff),
(isCheckout, ComposableAlwaysOn),
(isAnything, ComposableTraceIDRatio(0.1))
])
// Create a parent-based sampler for child spans
finalSampler = ComposableParentThreshold(rootSampler)
此示例创建一个配置,其中
- 健康检查端点永远不会被采样
- 结账端点始终被采样
- 其他根 Span 以 10% 的概率采样
- 子 Span 遵循其父项的采样决策
采样要求
状态: 开发中
W3C Trace Context Level 2 候选推荐包含 随机 Trace ID 标志,用于指示 TraceID 包含 56 个随机位,这是为统计目的指定的。此标志表明 TraceID 的最低有效(“最右侧”)7 字节或 56 位是随机的。
请注意,随机标志不会通过不识别该标志的 Trace Context Level 1 实现进行传播。当此标志为 1 时,它被认为是有效的。当此标志为 0 时,可能是由于 TraceID 非随机,或者因为使用了 Trace Context Level 1 传播器。为了在此类以及 TraceID 缺乏足够随机性的其他情况启用采样,OpenTelemetry 定义了一个可选的 显式随机值,该值编码在 W3C TraceState 字段中。
本规范建议使用 TraceID 随机性或显式随机性,以确保在使用 W3C Trace Context 传播时,采样器始终具有足够的随机性。
TraceID 随机性
对于根 Span 上下文,SDK 在生成 TraceID 值时 SHOULD 实现 W3C Trace Context Level 2 候选推荐的 TraceID 随机性要求。
随机 Trace 标志
对于根 Span 上下文,当 SDK 生成满足 W3C Trace Context Level 2 随机性要求的 TraceID 时,SDK SHOULD 在 trace 标志中设置 Random 标志。
显式随机性
显式随机性是一种机制,它使用户和 SDK 作者能够控制跟踪随机性。以下建议适用于已忽略上述 TraceID 随机性建议的 Trace SDK。它包含两个部分。
不要覆盖显式随机性
API 用户控制根 Span 的初始 TraceState,因此他们可以通过定义 OpenTelemetry TraceState 的 rv 子键来为跟踪提供显式随机性。SDK 和 Samplers MUST NOT 覆盖 OpenTelemetry TraceState 值中的显式随机性。
根采样器为非随机 TraceID 设置显式随机性
当 SDK 生成的 TraceID 不满足 W3C Trace Context Level 2 随机性要求(由未设置的 trace random 标志指示),并且 OpenTelemetry TraceState 值中的 rv 子键尚未设置时,Root sampler 有机会插入显式随机值。
Root Samplers MAY 在尚未设置显式随机值的情况下,将显式随机值插入 OpenTelemetry TraceState 值。
例如,这是一个具有非随机标识符和显式随机值的 W3C Trace Context
traceparent: 00-ffffffffffffffffffffffffffffffff-ffffffffffffffff-00
tracestate: ot=rv:7479cfb506891d
TraceID 随机性假定
对于所有 Span 上下文,OpenTelemetry samplers SHOULD 假定 TraceIDs 满足 W3C Trace Context Level 2 随机性要求,除非在 OpenTelemetry TraceState 的 rv 子键中存在显式随机值。
IdGenerator 随机性
如果 SDK 使用 IdGenerator 扩展点,SDK SHOULD 允许该扩展程序在生成新 ID 时确定是否设置了 Random 标志。
Span 限制
Span 属性 MUST 符合 属性限制的通用规则。
SDK Spans MAY 还丢弃链接和事件,以防止每个集合的元素数量超过配置的限制。
如果 SDK 实现了上述限制,则 MUST 提供一种方法来更改这些限制,通过 TracerProvider 的配置,允许用户配置单个限制,例如下面的 Java 示例。
配置选项的名称 SHOULD 为 EventCountLimit 和 LinkCountLimit。这些选项 MAY 被捆绑在一个类中,该类 SHOULD 被命名为 SpanLimits。实现 MAY 提供额外的配置,如 AttributePerEventCountLimit 和 AttributePerLinkCountLimit。
public final class SpanLimits {
SpanLimits(int attributeCountLimit, int linkCountLimit, int eventCountLimit);
public int getAttributeCountLimit();
public int getAttributeCountPerEventLimit();
public int getAttributeCountPerLinkLimit();
public int getEventCountLimit();
public int getLinkCountLimit();
}
Configurable parameters
- 适用于属性的所有通用选项
EventCountLimit(默认=128) - 允许的最大 Span 事件计数;LinkCountLimit(默认=128) - 允许的最大 Span 链接计数;AttributePerEventCountLimit(默认=128) - 每个 Span 事件允许的最大属性计数;AttributePerLinkCountLimit(默认=128) - 每个 Span 链接允许的最大属性计数;
SDK 的日志中 SHOULD 打印一条消息,告知用户属性、事件或链接因此类限制而被丢弃。为防止日志过多,该消息 MUST 最多每个 Span 打印一次(即,不是每个被丢弃的属性、事件或链接)。
ID 生成器
SDK MUST 默认随机生成 TraceId 和 SpanId。
SDK MUST 提供一种机制来定制 TraceId 和 SpanId 的 ID 生成方式。
SDK MAY 通过允许自定义实现一个接口(如下面的 Java 示例)来提供此功能(接口名称 MAY 为 IdGenerator,方法名称 MUST 与 SpanContext 一致),该接口为两个方法提供了扩展点:一个用于生成 SpanId,另一个用于生成 TraceId。
public interface IdGenerator {
byte[] generateSpanIdBytes();
byte[] generateTraceIdBytes();
}
其他实现供应商特定协议(如 AWS X-Ray trace id generator)的 IdGenerator MUST NOT 在核心 OpenTelemetry 仓库中维护或分发。
IdGenerator 随机性
状态: 开发中
IdGenerator 的自定义实现 SHOULD 在所有生成的 TraceID 值都满足 W3C Trace Context Level 2 随机性要求时进行适当标识,以便 Trace random 标志将设置在相关的 Trace 上下文中。这被假定为 IdGenerator 实现的一个静态属性,可以使用语言特性来推断,例如通过扩展一个标记接口。
Span processor
Span processor 是一个接口,允许在 Span 开始和结束方法调用时进行钩子。Span processors 仅在 IsRecording 为 true 时调用。
内置的 span processors 负责将 Span 批处理和转换为可导出表示,并将批次传递给 exporters。
Span processors 可以直接注册在 SDK 的 TracerProvider 上,并且它们按注册顺序调用。
注册在 TracerProvider 上的每个 processor 都是一个管道的开始,该管道由 span processor 和可选的 exporter 组成。SDK MUST 允许每个管道以单独的 exporter 结束。
SDK MUST 允许用户实现和配置自定义 processors。
下图显示了 SpanProcessor 与 SDK 中其他组件的关系
+-----+--------------+ +-------------------------+ +----------------+
| | | | | | |
| | | | Batching Span Processor | | SpanExporter |
| | +---> Simple Span Processor +---> (OTLPExporter) |
| | | | | | |
| SDK | Span.start() | +-------------------------+ +----------------+
| | Span.end() |
| | |
| | |
| | |
| | |
+-----+--------------+
接口定义
SpanProcessor 接口 MUST 声明以下方法
SpanProcessor 接口 SHOULD 声明以下方法
- OnEnding 方法。
OnStart
OnStart 在 Span 开始时被调用。此方法在启动 Span 的线程上同步调用,因此它不应阻塞或抛出异常。如果注册了多个 SpanProcessors,它们的 OnStart 回调会按注册顺序调用。
参数
span- 一个 读/写 Span 对象,用于已启动的 Span。SHOULD 能够保留对此 Span 对象的引用,并且对 Span 的更新 SHOULD 反映在其中。例如,这对于创建一个 SpanProcessor 非常有用,该处理器可以从后台线程定期评估/打印有关所有活动 Span 的信息。parentContext- SDK 确定的 Span 的父Context(显式传递的Context、当前Context或如果显式请求为空Context)。
返回:Void
OnEnding
状态: 开发中
OnEnding 在 Span End() 操作期间被调用。结束时间戳 MUST 已计算(OnEnding 方法的持续时间不计入 Span 持续时间)。在调用 OnEnding 时,Span 对象 MUST 仍可变(即,可以调用 SetAttribute、AddLink、AddEvent)。此方法 MUST 在 Span.End() API 内同步调用,因此不应阻塞或抛出异常。如果注册了多个 SpanProcessors,它们的 OnEnding 回调将按注册顺序调用。SDK MUST 确保在调用第一个 SpanProcessor 的 OnEnding 之前,Span 不会被任何其他线程修改。从那时起,修改仅允许从调用 的 OnEnding 回调中同步进行。所有注册的 SpanProcessor OnEnding 回调将在任何 SpanProcessor 的 OnEnd 回调被调用之前执行。
参数
span- 一个 读/写 Span 对象,用于即将结束的 Span。
返回:Void
OnEnd(Span)
OnEnd 在 Span 结束之后被调用(即,结束时间戳已设置)。此方法 MUST 在 Span.End() API 内同步调用,因此不应阻塞或抛出异常。
参数
Span- 一个 可读 Span 对象,用于已结束的 Span。注意:即使传入的 Span 在技术上可能是可写的,但由于此时它已结束,因此不允许修改它。
返回:Void
Shutdown()
关闭 processor。在 SDK 关闭时调用。这是 processor 执行任何必要清理的机会。
Shutdown 应该只为每个 SpanProcessor 实例调用一次。调用 Shutdown 后,不允许后续调用 OnStart、OnEnd 或 ForceFlush。SDKs SHOULD 尽可能优雅地忽略这些调用。
Shutdown 应提供一种方式,让调用者知道它是否成功、失败或超时。
Shutdown MUST 包含 ForceFlush 的效果。
Shutdown 应在某个超时时间内完成或中止。Shutdown 可以实现为阻塞 API 或异步 API,通过回调或事件通知调用者。OpenTelemetry 客户端作者可以决定是否要使 shutdown 超时可配置。
ForceFlush()
这是为了确保 SpanProcessor 在调用 ForceFlush 之前已经收到事件的所有 Spans 的相关任务 SHOULD 尽快完成,最好是在此方法返回之前。
特别是,如果任何 SpanProcessor 具有任何关联的 exporter,它 SHOULD 尝试调用 exporter 的 Export(针对所有尚未执行此操作的 Span),然后在其上调用 ForceFlush。 内置 SpanProcessors MUST 这样做。如果指定了超时(见下文),SpanProcessor MUST 优先考虑遵守超时而不是完成所有调用。它 MAY 跳过或中止它已进行的某些或所有 Export 或 ForceFlush 调用以实现此目标。
ForceFlush 应提供一种方式,让调用者知道它是否成功、失败或超时。
ForceFlush SHOULD 仅在绝对必要时调用,例如在使用某些可能在调用后暂停进程但在此之前 SpanProcessor 导出已完成 Span 的 FaaS 提供程序时。
ForceFlush 应在某个超时时间内完成或中止。ForceFlush 可以实现为阻塞 API 或异步 API,通过回调或事件通知调用者。OpenTelemetry 客户端作者可以决定是否要使 flush 超时可配置。
内置 span processors
标准 OpenTelemetry SDK MUST 实现简单和批处理处理器,如下所述。其他常见的处理场景应首先在 OpenTelemetry Collector 中进行进程外实现。
Simple processor
这是 SpanProcessor 的一个实现,它在 Span 完成后立即将完成的 Span 和可导出友好的 Span 数据表示传递给配置的 SpanExporter。
该 processor MUST 同步调用 Span Exporter 的 Export,以确保它们不会并发调用。
Configurable parameters
exporter- 将 Span 推送到此处的 exporter。
Batching processor
这是 SpanProcessor 的一个实现,它创建完成 Span 的批次,并将可导出友好的 Span 数据表示传递给配置的 SpanExporter。
该 processor MUST 同步调用 Span Exporter 的 Export,以确保它们不会并发调用。
当发生以下任一情况 AND 上一次导出调用已返回时,processor SHOULD 导出批次
scheduledDelayMillis在 processor 构建后 OR processor 收到第一个 Span 后。scheduledDelayMillis在上一次导出计时器结束 OR 上一次导出完成之后,OR 在上一次导出计时器结束或上一次批次完成后,第一个 Span 被添加到队列中。- 队列包含
maxExportBatchSize或更多 Span。 - 调用了
ForceFlush。
如果触发导出时队列为空,processor MAY 导出空批次 OR 跳过导出并认为其已立即完成。
Configurable parameters
exporter- 将 Span 推送到此处的 exporter。maxQueueSize- 最大队列大小。达到此大小后,Span 将被丢弃。默认值为2048。scheduledDelayMillis- 两次连续导出之间的最大延迟时间(以毫秒为单位)。默认值为5000。exportTimeoutMillis- 导出可以运行多长时间才会被取消。默认值为30000。maxExportBatchSize- 每次导出的最大批次大小。它必须小于或等于maxQueueSize。如果队列达到maxExportBatchSize,即使scheduledDelayMillis毫秒尚未过去,也会导出批次。默认值为512。
Span Exporter
Span Exporter 定义了特定协议的 exporters 必须实现的接口,以便它们可以插入 OpenTelemetry SDK 并支持遥测数据的发送。
该接口的目标是最小化协议相关遥测 exporter 的实现负担。协议 exporter 主要被期望是一个简单的遥测数据编码器和传输器。
每个实现 MUST 记录 SDK 对 exporter 的并发性要求。
接口定义
exporter MUST 支持三个函数:Export、Shutdown 和 ForceFlush。在强类型语言中,通常每个信号都有一个单独的 Exporter 接口(SpanExporter、...)。
Export(batch)
导出 可读 Span 的批次。实现此函数的协议 exporter 通常期望将数据序列化并传输到目的地。
Export() 不应与同一 exporter 实例的其他 Export 调用并发调用。
根据实现,导出的结果可能不会在 Export() 调用返回值中返回给 Processor,而是以特定于语言的方式用于通知异步任务的完成。这意味着,虽然 exporter 实例永远不应被并发调用 Export(),但这并不意味着导出任务不能并发进行。如何完成超出了本规范的范围。
Export() MUST 不会无限期阻塞,必须有一个合理的上限,在此之后调用必须以错误结果(Failure)超时。
并发请求和重试逻辑由 exporter 负责。默认 SDK 的 Span Processors SHOULD 不实现重试逻辑,因为所需的逻辑可能高度依赖于发送 Span 的特定协议和后端。例如,OpenTelemetry Protocol (OTLP) 规范定义了发送并发请求和重试请求的逻辑。
参数
batch - 可读 Span 的批次。批次的具体数据类型是特定于语言的,通常是某种列表,例如 Java 中的 Span 通常是 Collection<SpanData>。
返回: ExportResult
Export() 的返回值是特定于实现的。对于语言习惯法,Exporter 必须向 Processor 发送一个 ExportResult。 ExportResult 的值是 Success 或 Failure。
Success- 批次已成功导出。对于协议导出器,这通常意味着数据已通过线路发送并传递到目标服务器。Failure- 导出失败。批次必须被丢弃。例如,当批次包含错误数据且无法序列化时,可能会发生这种情况。
例如,在 Java 中,Export() 的返回值将是一个 Future,完成后返回 ExportResult 对象。而在 Erlang 中,Exporter 会向 Processor 发送一条消息,其中包含特定 Span 批次的 ExportResult。
Shutdown()
关闭 exporter。在 SDK 关闭时调用。这是 exporter 执行任何必要清理的机会。
Shutdown 应该只为每个 Exporter 实例调用一次。调用 Shutdown 后,不允许后续调用 Export,并且应返回 Failure 结果。
Shutdown 不应无限期阻塞(例如,如果它尝试刷新数据但目标不可用)。OpenTelemetry 客户端作者可以决定是否要使 shutdown 超时可配置。
ForceFlush()
这是为了确保 exporter 在调用 ForceFlush 之前收到的任何 Spans 的导出 SHOULD 尽快完成,最好是在此方法返回之前。
ForceFlush 应提供一种方式,让调用者知道它是否成功、失败或超时。
ForceFlush SHOULD 仅在绝对必要时调用,例如在使用某些可能在调用后暂停进程但在此之前 exporter 导出已完成 Span 的 FaaS 提供程序时。
ForceFlush 应在某个超时时间内完成或中止。ForceFlush 可以实现为阻塞 API 或异步 API,通过回调或事件通知调用者。OpenTelemetry 客户端作者可以决定是否要使 flush 超时可配置。
进一步的语言特化
基于上面概述的通用接口定义,库作者必须为特定语言定义确切的接口。
鼓励作者使用高效的数据结构作为接口边界,这些数据结构非常适合由协议 exporter 快速序列化为线格式,并最大程度地减少对内存管理器的压力。后者通常需要了解如何优化快速生成、短暂的遥测数据结构,以使特定语言的内存管理器更轻松。通用建议是最小化分配数量并在可能的情况下使用分配池,从而避免在高数据生成率下出现分配/去分配/收集操作的爆炸。
示例
这些是关于 Exporter 接口在特定语言中可能是什么样子的示例。示例仅用于说明目的。OpenTelemetry 客户端作者可以自由偏离这些,前提是他们的设计保持 Exporter 概念的精神。
Go SpanExporter 接口
type SpanExporter interface {
Export(batch []ExportableSpan) ExportResult
Shutdown()
}
type ExportResult struct {
Code ExportResultCode
WrappedError error
}
type ExportResultCode int
const (
Success ExportResultCode = iota
Failure
)
Java SpanExporter 接口
public interface SpanExporter {
public enum ResultCode {
Success, Failure
}
ResultCode export(Collection<ExportableSpan> batch);
void shutdown();
}