Grafana Tempo 应用洞察实践:用 Span Metrics 识别性能瓶颈并建立 SLO
【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo
Traces 为应用性能提供了直接的可观测窗口,而 Tempo 的 metrics-generator 组件能够把这些 Trace 数据转化为开箱即用的 RED(Rate、Error、Duration)指标,让你在不需要额外埋点的情况下监控服务延迟、错误率与吞吐量。本篇技术指南以虚构电商公司 Handy Site Corp 的实战场景为主线,完整讲解如何基于 Tempo 生成的 span metrics 定义合理的 SLO(服务级别目标)、利用 exemplar 从指标一键跳转到代表性 Trace,并借助 Grafana SLO 应用建立基于 SLI 的告警体系。读完本文,你将掌握traces_spanmetrics_latency等核心指标的标签结构与查询方式、metrics-generator 的完整配置项(自定义维度、维度映射、过滤策略、采样率补偿等),以及源码级别的实现原理,能够直接在自己的 Tempo 部署中落地一套可量化的服务质量监控方案。
从 Trace 到应用洞察:为什么需要用 Trace 驱动性能分析
Traces 是一扇观察应用性能的窗口,它记录了单个请求在一次分布式调用中经历的每一个环节。借助 Trace 数据,你能够:
- 定位系统中的瓶颈,找到拖慢整体响应时间的具体服务或操作;
- 发现潜在的性能优化点,降低延迟与响应时间,从而提升应用吞吐量;
- 在故障发生时快速完成问题分诊(triage),判断问题出在哪个服务、哪条调用链上。
但原始 Trace 数据量巨大且以请求为粒度,很难直接回答"这段时间整体延迟如何"这样的聚合问题。Tempo 提供的解法是:在服务端用metrics-generator组件直接从 Trace 生成指标,让"洞察"变成可持续观测的、可告警的、可量化评估的数据。
场景引入:Meet Handy Site Corp
为了说明完整思路,官方文档引入了一个虚构公司 Handy Site Corp:一家运营电商应用的网站公司,其系统由用户认证(authentication)、商品目录(catalog)、订单管理(order management)、支付处理(payment processing)等多个服务组成。
这类微服务系统正是 Trace 数据的典型来源:一次结账请求会跨越多个服务,任何一个环节变慢都会直接拖累用户体验。因此,Handy Site 的工程师选择从 Trace 出发,围绕结账流程的延迟建立可度量的服务质量体系。
定义现实可行的 SLO
Handy Site 的工程师首先为结账流程建立围绕延迟的服务级别目标(SLO)。一个正确的 SLO 应当是基于正常运营时期的历史数据推算出来的现实目标,而不是拍脑袋定下的理想值——只有贴近真实水平的 SLO 才具有监控和告警的意义。
确定 SLO 之后,团队会配置告警,用于在"有风险无法达成该目标"时及时发出信号。SLO 告警的价值在于:它只在 SLI 的当前取值表明团队面临失守风险时才触发,从而避免传统阈值告警带来的告警噪音。
使用 Span Metrics 定义 SLI
Handy Site 在评估后,决定使用span metrics(跨度指标)作为服务级别指标(SLI)来衡量 SLO 的达成情况。SLI 是 SLO 的量化载体,它把"用户是否获得良好体验"翻译成可测量、可追踪的指标值。
Tempo 使用 metrics-generator 组件从 Trace 生成指标。这些指标基于传入 Trace 中的 span 创建,开箱即用地反映应用流程与整体概貌,其中包括 RED 指标——Rate(请求速率)、Error(错误率)和 Duration(持续时间/延迟)。这意味着即使你的系统没有预先接入传统的指标采集体系,只要实现了分布式追踪,就能从追踪管道中获得现成的应用级指标。
Span Metrics:metrics-generator 的核心产出物
要理解 SLI 的构造,首先需要完整了解 span metrics 生成了哪些指标。根据官方文档与源码,span metrics 处理器(span metrics processor)会为每一种"维度组合"计算 span 的总数与持续时间,维度可以是服务名、操作名、span 类型、状态码,以及 span 上的任意属性(attribute)。这些指标的定义在源码中有明确对应,见 spanmetrics.go:
| 指标名 | 类型 | 标签 | 描述 |
|---|---|---|---|
traces_spanmetrics_latency | Histogram | 维度标签 | span 的持续时间(延迟分布) |
traces_spanmetrics_calls_total | Counter | 维度标签 | span 的总调用次数 |
traces_spanmetrics_size_total | Counter | 维度标签 | 摄入 span 的总大小(字节) |
从源码可以看到这三个子处理器(subprocessor)的默认开关状态:Latency、Count、Size默认全部启用(见 config.go),因此默认部署下三个指标都会生成。如果你只需要其中部分指标,可以在 overrides 配置中通过子处理器单独控制,例如只保留延迟直方图与调用次数计数器:
overrides: defaults: metrics_generator: processors: - span-metrics-latency - span-metrics-count # span-metrics-size 被省略,即关闭 size 指标默认标签:一个指标上的内建维度
metrics-generator 会为生成的每个指标附加一组默认标签(intrinsic dimensions),包括service、span_name、span_kind和status_code。这些标签的含义如下:
service— 产生该 span 的服务名称;span_name— span 的唯一名称(即操作名);span_kind— span 的类型,取值为五种之一:SPAN_KIND_SERVER— 该 span 由来自其他服务的调用生成;SPAN_KIND_CLIENT— 该 span 发起了对另一个服务的调用;SPAN_KIND_INTERNAL— 该 span 在其所在服务内部完成,无外部交互;SPAN_KIND_PRODUCER— 该 span 创建了推送到总线或消息代理的数据;SPAN_KIND_CONSUMER— 该 span 消费了总线或消息系统上的数据;
status_code— span 的结果,取值为三种之一:STATUS_CODE_UNSET— 结果未设置/未知;STATUS_CODE_OK— span 操作成功完成;STATUS_CODE_ERROR— span 操作以错误结束。
源码中的aggregateMetricsForSpan函数展示了这些标签如何从每个 span 上提取并加入指标序列(见 spanmetrics.go),而spanKindString/statusCodeString两个函数则在热路径上把 OTLP 枚举转换为可读的标签字符串。
除此之外还有三个可选标签,默认不开启:
status_message(可选)— 描述status_code原因的消息,需要显式开启;job— 命名空间与服务的组合名称,仅在metrics_generator.processor.span_metrics.enable_target_info: true时添加;instance— 实例 ID,仅在enable_target_info: true且enable_instance_label: true时添加。
Exemplar:从指标一键跳回 Trace
span metrics 还让 exemplar 的使用变得异常简单。Exemplar 是聚合到某个指标观测中的一个详细样本:它包含观测值本身,以及可选的时间戳和任意 Trace ID——通常正是用于关联到某条 Trace 的 ID。
由于 Trace 与指标在 metrics-generator 内部共存,exemplar 可以自动附加到生成的指标上。这带来两个非常实用的工作流:
- 从展示"延迟随时间聚合"的指标图,快速跳转到代表低延迟、中延迟或高延迟请求的某条具体 Trace;
- 从展示"错误率随时间变化"的指标图,快速跳转到出错的某条具体 Trace。
在源码层面,exemplar 由直方图实现承载:histogram.go中每个 bucket 都维护了 exemplar 与对应观测值(见 histogram.go),而 span metrics 处理器在观测延迟时会传入 span 的TraceId(见 spanmetrics.go),从而把"哪个请求贡献了这个观测值"记录下来。这就是从指标图点击 exemplar 直接进入 Trace 详情的底层机制。
监控延迟:用 traces_spanmetrics_latency 构造 SLI
回到 Handy Site 的场景。团队最关心结账服务(checkout service)处理的请求延迟,并设定目标:一个月内 99.5% 的请求应在 2 秒内完成。
为了构造能够追踪该目标的 SLI,他们使用traces_spanmetrics_latency指标,并配合适当的标签选择器,例如service name = checkoutservice。由于 metrics-generator 默认已为指标添加span_kind、status_code等标签,查询可以直接基于service标签过滤出结账服务的数据,再按延迟分布评估达标比例。
如果团队想进一步按端点(endpoint)或软件版本分别统计结账服务延迟,可以修改 Tempo metrics-generator 的配置,把这些自定义维度(custom dimensions)作为标签加入 span metrics。
添加自定义维度
自定义维度通过dimensions配置项添加,其值取自 span 或 resource 上的属性。完整的配置结构位于metrics_generator.processor.span_metrics之下,例如:
metrics_generator: processor: span_metrics: dimensions: - http.method - http.route # 按端点拆分延迟 - deployment.environment需要留意的细节:
- 当某个配置的维度与默认标签冲突时(例如配置了
status_code),该维度生成的标签会被加上双下划线前缀,变成__status_code; - 在 Prometheus 标签名转换后允许重复维度,以便兼容不同埋点库的不同命名习惯。例如可以同时配置
deployment.environment与deployment_environment(两者都会转换为deployment_environment),发生冲突时最后配置的值生效; - 维度越多,生成指标的基数(cardinality)越高,需要在洞察力与资源消耗之间做权衡。
重命名与组合维度:dimension_mappings
如果希望把属性重命名为自定义标签名,或把多个属性合并成一个复合标签,可以使用dimension_mappings配置。它的source_labels必须填写原始 span/resource 属性名(带点号,如deployment.environment),而不是清洗后的 Prometheus 标签名;name字段中点和下划线都会被统一转换为下划线。
例如,把deployment.environment重命名为更短的env标签:
dimension_mappings: - name: env source_labels: ["deployment.environment"]再例如,把service.name、service.namespace、service.version组合成单个标签service_instance,join参数指定连接符:
dimension_mappings: - name: service_instance source_labels: ["service.name", "service.namespace", "service.version"] join: "/"当 span 携带service.name = "abc"、service.namespace = "def"、service.version = "ghi"时,生成的指标标签即为service_instance="abc/def/ghi"。这与源码中aggregateMetricsForSpan对DimensionMappings的拼接逻辑一致(见 spanmetrics.go)。
控制默认内建维度以降低基数
如果某些默认标签不需要,可以通过intrinsic_dimensions关闭它们,从而降低指标基数:
metrics_generator: processor: span_metrics: intrinsic_dimensions: service: true span_name: true span_kind: false # 关闭 span_kind 标签 status_code: true status_message: false # 默认即关闭处理采样后的 Trace:span multiplier
如果你在 Trace 进入 Tempo 之前使用了基于比例的采样器,指标计数会因此失真。metrics-generator 提供两种补偿方案:
方案一:自定义 span 属性。在采样器中把采样率写入 span 属性,例如X-SampleRatio,然后在配置中指定:
metrics_generator: processor: span_metrics: span_multiplier_key: "X-SampleRatio"方案二:W3C tracestate 阈值。如果采样器使用 OpenTelemetry 的th(threshold)子键把采样概率记录在 W3C tracestate 头中,可以开启自动提取:
metrics_generator: processor: span_metrics: enable_tracestate_span_multiplier: true当 tracestate 缺失或无效时,配置会回退到基于属性的方案。源码中usesSpanMultiplier与GetSpanMultiplier的处理逻辑见 spanmetrics.go。
用过滤策略裁剪指标
在部分场景下你可能只想为特定流量生成指标,这可以通过filter_policies实现。支持include(逻辑 AND)、include_any(逻辑 OR,命中即包含)与exclude(命中即排除)三类策略,match_type支持strict(严格比较)与regex(正则匹配),过滤属性支持bool、double、int、string类型,以及name、status、kind等内建 span 属性。
例如只保留 kind 为 server 的 span:
metrics_generator: processor: span_metrics: filter_policies: - include: match_type: strict attributes: - key: kind value: SPAN_KIND_SERVER非内建属性需要按 TraceQL 的语法指定作用域,例如resource.location。更复杂的组合可以实现"包含 EU 生产环境的所有 span、但额外放行 auth-service 的内部 span、同时剔除 dev 环境"这类灵活策略:
metrics_generator: processor: span_metrics: filter_policies: # 只处理来自 EU 生产环境的 span - include: match_type: regex attributes: - key: resource.location value: eu-.* # 例外规则:允许 auth-service 的 INTERNAL span - include_any: match_type: strict attributes: - key: kind value: SPAN_KIND_INTERNAL - key: resource.service.name value: auth-service # 丢弃所有开发环境 tier 的 span - exclude: match_type: regex attributes: - key: resource.tier value: dev-.*完整的指标定义、标签说明与配置示例,可进一步阅读仓库中的官方文档 span-metrics-metrics-generator.md 与 metrics-generator 总览。
在 Grafana 中建立 SLO 与基于风险的告警
一切就绪后,Handy Site 打开 Grafana 的SLO 应用,按照设置流程为结账服务围绕traces_spanmetrics_latency指标建立 SLI。要点如下:
- SLI 定义:使用
traces_spanmetrics_latency直方图,配合service="checkoutservice"等标签选择器,量化"2 秒内完成请求"的比例; - SLO 目标:把 99.5% 的月度达标率作为服务级别目标;
- 告警信号:基于 SLO 的告警只在 SLI 取值表明团队有失守风险时触发,因此既能及时暴露直接影响用户体验的服务质量劣化,又不会产生传统固定阈值告警那样的噪音告警。
这种告警模式在可观测性实践中被称为"基于风险的告警"(SLO-based alerting):告警不再是"某个指标超过阈值",而是"照当前趋势发展,我们可能无法兑现对用户的承诺"。它天然把告警与业务影响绑定在一起,也让排障优先级变得清晰——当支付服务延迟上升导致 SLO 失守风险增加时,团队立刻知道该处理哪条链路。
源码级原理:Span Metrics 处理器的内部机制
最后,从源码层面梳理 span metrics 处理器的完整工作链路,帮助你理解上述每个配置项的实际作用点:
入口:
PushSpans接收 OTLP 格式的 span 批次,交由aggregateMetrics处理(见 spanmetrics.go)。在 Tempo 3.0 微服务部署中,metrics-generator 改为从 Kafka 消费 Trace 数据;单二进制部署中 distributor 仍在进程内调用PushSpans。资源级预处理:每个 resource 批次只提取一次
service.name(即 job 名)与实例 ID,避免在单个 span 的热路径上重复计算。过滤:每个 span 先经过
filter_policies判定,不满足策略的 span 被跳过并计入filteredSpansCounter。标签构建:
aggregateMetricsForSpan依次加入内建维度(service、span_name、span_kind、status_code、可选的 status_message)、自定义dimensions、dimension_mappings生成的标签,以及可选的job/instance标签。指标更新:根据启用的子处理器分别更新计数器与直方图。延迟计算基于 span 的
StartTimeUnixNano与EndTimeUnixNano,负延迟按 0 处理(见 spanmetrics.go);直方图观测时携带 span 的TraceId以生成 exemplar。基数控制:指标基数由"维度组合的数量"决定。默认的直方图桶为指数桶(起始 0.002 秒、步长 2 倍、共 14 个桶),在启用自定义维度前应通过 cardinality 文档 评估基数影响。
输出:metrics-generator 内部运行一个 Prometheus Agent,通过可配置的
remote_write端点把指标推送到 Prometheus 或兼容后端(如 Grafana Mimir);写入间隔由metrics_generator.registry.collection_interval控制。多租户场景下会透传X-Scope-OrgID请求头,可通过remote_write_add_org_id_header: false关闭。
小结
本文以 Handy Site Corp 的电商场景为线索,完整演示了"用 Trace 驱动应用洞察"的落地路径:先基于历史数据定义现实可行的 SLO,再以 metrics-generator 生成的 span metrics 作为 SLI,通过traces_spanmetrics_latency等指标配合标签选择器量化延迟表现,借助 exemplar 在指标与 Trace 之间自由穿梭,最后在 Grafana SLO 应用中建立基于风险的告警。同时,通过dimensions、dimension_mappings、filter_policies、span_multiplier_key等配置,你可以把这套方案精确裁剪到自己的服务拓扑、版本管理与采样策略之上。相关配置的完整参考与更多高级选项,可继续阅读仓库文档 metrics-generator 配置、架构与处理器总览 以及源码 spanmetrics.go 与 config.go。
【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考