4. 基于调用链的错误SLI口径定义:如何通过Span的Error Tag或StatusCode定义请求成功率/错误率SLI
4.1 错误SLI的核心数据来源
在OpenTelemetry与Jaeger的调用链模型中,Span是记录请求处理状态的最小单元。判断一次请求是否“错误”,主要依赖Span携带的两个关键属性:
- Status Code:Span的最终状态码,取值
Unset、Ok、Error。 - Error Tag:布尔型标签
error=true,通常由SDK自动或手动设置。
错误SLI(Service Level Indicator)即基于这两个字段,统计“错误Span数 / 总Span数”或“错误请求数 / 总请求数”,从而得到请求成功率或错误率。
关键原则: 错误SLI的口径必须与业务语义对齐。并非所有
error=true 的Span都代表用户可感知的失败,例如4xx状态码在某些场景下属于正常业务逻辑。
4.2 错误判定字段对比
| 字段 | 类型 | 设置方式 | 典型值 | 适用场景 |
|---|---|---|---|---|
| Status Code | 枚举(Unset/Ok/Error) | SDK自动设置或手动调用 span.setStatus() |
StatusCode.ERROR |
明确区分成功/失败,适合后端服务 |
| Error Tag | 布尔(true/false) | SDK自动捕获异常时设置,或手动 span.setAttribute("error", true) |
error=true |
兼容旧版OpenTracing语义,适合异常捕获 |
推荐做法: 优先使用
Status Code 作为错误判定依据,因为它语义更清晰、标准化程度更高。Error Tag 可作为辅助信号,用于标记未抛出异常但业务逻辑失败的场景。
4.3 错误SLI口径定义流程
定义错误SLI需要经过以下步骤,确保口径可测量、可复现:
- 确定错误判定规则:选择基于
Status Code还是Error Tag,或两者组合。 - 定义错误Span范围:是否包含所有Span?还是仅限根Span(Root Span)或特定服务Span?
- 排除非业务错误:例如健康检查、探活请求、预发流量等,需通过标签或服务名过滤。
- 设定统计窗口:固定时间窗口(如1分钟、5分钟)或滑动窗口。
- 计算SLI值:错误率 = 错误Span数 / 总Span数 × 100%;成功率 = 1 - 错误率。
常见陷阱: 直接使用Jaeger UI中的“Error”筛选结果作为SLI数据源,可能包含重复计数或非业务错误,导致口径偏差。必须基于原始Span数据做二次清洗。
4.4 错误SLI口径定义示例(基于OpenTelemetry语义)
以下是一个典型的错误SLI查询逻辑(以PromQL或SQL风格描述):
-- 假设Span数据已存入ClickHouse或类似时序数据库
-- 统计每分钟的错误率(基于Status Code)
SELECT
toStartOfMinute(Timestamp) AS minute,
countIf(StatusCode = 'Error') AS error_count,
count(*) AS total_count,
round(error_count / total_count * 100, 2) AS error_rate_percent
FROM otel_spans
WHERE ServiceName = 'order-service'
AND SpanKind = 'SERVER' -- 只统计服务端Span
AND Attributes['http.method'] != 'GET /health' -- 排除健康检查
GROUP BY minute
ORDER BY minute DESC;
4.5 错误SLI口径基线校准要点
- 基线校准周期:建议以7天或30天为窗口,计算历史错误率均值作为基线。
- 异常阈值设定:基线 ± 3σ 或固定百分比(如错误率超过0.1%触发告警)。
- 口径变更管理:每次修改错误判定规则后,需重新计算基线,避免新旧口径混用。
- 多服务统一口径:不同服务可能使用不同的HTTP状态码语义,需在SLI计算层做归一化处理。
4.6 错误SLI口径定义流程SVG
4.7 错误SLI口径基线校准示例(基于Jaeger数据)
假设我们有一个微服务 payment-service,需要定义其请求成功率SLI。以下是基于Jaeger调用链数据的口径定义示例:
| 维度 | 口径定义 | 说明 |
|---|---|---|
| 数据源 | Jaeger Span(OpenTelemetry格式) | 仅采集 SpanKind = SERVER 的根Span |
| 错误判定 | StatusCode = Error 或 error=true |
两者满足其一即视为错误Span |
| 排除规则 | 排除 http.target = "/health" 或 http.method = "OPTIONS" |
避免探活和预检请求干扰 |
| 统计窗口 | 1分钟滑动窗口 | 每1分钟计算一次错误率 |
| 基线计算 | 过去7天同一小时的历史错误率均值 | 消除时间周期性影响 |
| 告警阈值 | 基线 + 3倍标准差 | 超过阈值触发告警 |
实战建议: 在Jaeger中,可以通过
tags.error = true 快速筛选错误Span,但建议同时检查 status.code 字段,避免遗漏手动设置 StatusCode.ERROR 但未打Error Tag的Span。
4.8 错误SLI口径定义中的常见误区
- 误区一: 认为所有
error=true的Span都代表用户请求失败。实际上,某些内部重试或补偿逻辑也会产生错误Span,但最终请求可能成功。 - 误区二: 忽略
StatusCode = Unset的Span。在OpenTelemetry中,Unset表示状态未知,不应默认视为成功。 - 误区三: 直接使用Jaeger UI中的“Error Rate”图表作为SLI。该图表可能包含所有Span类型,未做业务过滤。
- 误区四: 口径变更后未重新校准基线,导致新旧口径混用,告警阈值失效。
4.9 错误SLI口径定义最佳实践
- 统一错误语义:团队内部约定
StatusCode.Error为唯一错误判定标准,error=true仅作为辅助标记。 - 分层定义SLI:为每个服务定义独立的错误SLI口径,因为不同服务的业务语义不同。
- 自动化口径校验:通过CI/CD流水线自动检查Span数据中的错误标记是否符合口径规范。
- 定期复盘口径:每季度或每次重大发布后,重新审视错误SLI口径是否仍然适用。
工具推荐: 使用OpenTelemetry Collector的
spanmetrics 处理器,可以直接在采集端生成基于Span的错误率指标,减少后处理复杂度。