上一篇文章我们用 MDC + TraceId 实现了单服务内的日志串联,但在微服务架构中,一次请求可能跨越 5 个服务、10 个节点——每个服务都有自己的 TraceId,日志散落在不同机器上,你无法还原完整的调用链路。OpenTelemetry 正是为此而生。本文将基于 OpenTelemetry Java Agent(零代码侵入) ,从零搭建全链路追踪体系,配合 Jaeger 实现调用链可视化,并打通 W3C Trace Context 标准实现跨服务透传。
一、为什么需要 OpenTelemetry?
在 OpenTelemetry 出现之前,分布式追踪领域是诸侯割据的局面:
| 追踪系统 | 传播格式 | 客户端库 |
|---|---|---|
| Jaeger | Uber-Trace-Id | Jaeger Client |
| Zipkin | B3 Propagation | Brave |
| SkyWalking | SW6 | SkyWalking Agent |
每个追踪系统都有自己的 Header 格式和客户端库。如果你的服务 A 用 Jaeger、服务 B 用 Zipkin,TraceId 无法跨服务传递,整个调用链就断了。
OpenTelemetry 的出现统一了这一切。它是一个 CNCF 孵化项目,提供了一套厂商无关的 API、SDK 和工具,用于采集、处理和导出遥测数据(Traces、Metrics、Logs)。它支持 W3C Trace Context 标准,让不同语言的服务、不同厂商的观测后端能够无缝协作。

二、核心概念:Trace、Span、Propagator
在开始实战之前,先理清三个核心概念:
| 概念 | 定义 | 类比 |
|---|---|---|
| Trace | 一次完整请求在分布式系统中的全部处理过程 | 一次旅行的全程记录 |
| Span | Trace 中的一个独立操作单元(如一次 HTTP 调用、一次 SQL 查询) | 旅行中的每一个站点 |
| Propagator | 跨进程传递 Trace 上下文的机制(如 HTTP Header) | 站点间的车票 |
一个 Trace 由多个 Span 组成树形结构,每个 Span 包含:
- TraceId:整个链路的唯一标识
- SpanId:当前操作的唯一标识
- ParentSpanId:父操作的标识
- Attributes:键值对元数据(如 HTTP URL、状态码)
- Events:时间戳标记(如异常发生时刻)
W3C Trace Context 标准定义了跨服务传递的 HTTP Header 格式:
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
tracestate: congo=t61rcWkgMzE
其中:
4bf92f3577b34da6a3ce929d0e0e4736= TraceId(32 位十六进制)00f067aa0ba902b7= SpanId(16 位十六进制)01= 采样标志
延伸阅读:本站的 《Java 日志最佳实践 2026:从 SLF4J 到 ELK 全链路日志追踪》 介绍了 MDC + TraceId 的单服务日志串联。OpenTelemetry 将此能力扩展到分布式环境,让 TraceId 跨越服务边界自动传递。
三、零代码侵入:OpenTelemetry Java Agent
OpenTelemetry 提供了三种接入方式:
| 方式 | 代码改动 | 适用场景 |
|---|---|---|
| Java Agent(推荐) | 零改动 | 绝大多数应用,开箱即用 |
| OTel Spring Boot Starter | 少量配置 | 需要 GraalVM Native Image 支持 |
| 手动 SDK 埋点 | 编写代码 | 自定义 Span、Agent 不支持的框架 |
Java Agent 是最佳起点——它通过字节码增强技术,在 JVM 启动时动态注入埋点代码,自动插桩 150+ 主流库和框架,包括 Spring MVC、WebFlux、JDBC、JPA、Kafka、gRPC、HTTP Client 等。
3.1 下载 Agent
# 下载最新版 Java Agent JAR
curl -L -O https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
3.2 配置并启动应用
通过 JVM 启动参数 -javaagent 挂载 Agent,配合环境变量或系统属性进行配置:
java -javaagent:/path/to/opentelemetry-javaagent.jar \
-Dotel.resource.attributes=service.name=order-service,service.version=1.0.0,deployment.environment=production \
-Dotel.traces.exporter=otlp \
-Dotel.exporter.otlp.protocol=grpc \
-Dotel.exporter.otlp.endpoint=http://jaeger-collector:4317 \
-jar order-service.jar
配置参数说明:
| 参数 | 说明 | 示例 |
|---|---|---|
otel.resource.attributes | 服务标识(名称、版本、环境) | service.name=order-service |
otel.traces.exporter | 导出器类型 | otlp(OTLP 协议) |
otel.exporter.otlp.protocol | OTLP 传输协议 | grpc 或 http/protobuf |
otel.exporter.otlp.endpoint | 接收端地址 | http://jaeger-collector:4317 |
Agent 默认将数据发送到 http://localhost:4318(HTTP)或 http://localhost:4317(gRPC),你可以通过环境变量覆盖。
就这么简单——无需修改一行代码,你的 Spring Boot 应用就已经具备全链路追踪能力了。
四、部署 Jaeger:分布式追踪的可视化后端
Jaeger 是 Uber 开源的分布式追踪系统,CNCF 毕业项目,原生支持 OpenTelemetry 的 OTLP 协议。
4.1 开发环境:All-in-One 模式
最快速的启动方式:
docker run -d --name jaeger \
-e COLLECTOR_OTLP_ENABLED=true \
-p 4317:4317 \
-p 4318:4318 \
-p 16686:16686 \
jaegertracing/all-in-one:latest
4317:gRPC OTLP 接收端口4318:HTTP OTLP 接收端口16686:Jaeger UI 访问端口
启动后访问 http://localhost:16686 即可看到 Jaeger 查询界面。
4.2 生产环境:Helm 部署 Kubernetes
生产环境推荐使用 Helm Chart 部署:
# 添加 Jaeger Helm 仓库
helm repo add jaegertracing https://jaegertracing.github.io/helm-charts
helm repo update
# 创建命名空间
kubectl create namespace tracing
# 部署 Jaeger(使用 Elasticsearch 作为存储后端)
helm install jaeger jaegertracing/jaeger \
--namespace tracing \
--set provisionDataStore.cassandra=false \
--set storage.type=elasticsearch \
--set storage.elasticsearch.host=elasticsearch-master \
--set storage.elasticsearch.port=9200 \
--set collector.otlp.enabled=true
生产环境配置清单:
# jaeger-values.yaml
collector:
otlp:
enabled: true
grpc:
port: 4317
http:
port: 4318
service:
type: ClusterIP
query:
service:
type: ClusterIP
ingress:
enabled: true
hosts:
- jaeger.example.com
storage:
type: elasticsearch
elasticsearch:
host: elasticsearch-master
port: 9200
scheme: http
agent:
enabled: false # 使用 OTLP 直连 Collector,无需 Agent
部署:
helm install jaeger jaegertracing/jaeger -f jaeger-values.yaml --namespace tracing
五、跨服务 TraceId 自动透传(W3C Trace Context)
这是全链路追踪的核心能力。OpenTelemetry Java Agent 默认使用 W3C Trace Context 作为传播格式,自动在跨服务 HTTP 调用中注入和提取 traceparent 和 tracestate Header。
5.1 自动透传(Agent 默认行为)
当服务 A(通过 RestTemplate/WebClient/OkHttp)调用服务 B 时,Agent 自动:
- Inject(客户端) :从当前 Span 提取 Trace 上下文,写入 HTTP 请求头
- Extract(服务端) :从 HTTP 请求头读取 Trace 上下文,设置为当前 Span 的父级
无需任何代码,TraceId 自动跨越服务边界。
5.2 验证透传是否生效
在服务 A 的日志中打印 TraceId(需配置日志 MDC,参考本站 《Java 日志最佳实践》),调用服务 B 后,检查服务 B 的日志——TraceId 应该完全一致。
# 服务 A 日志
2026-07-19 10:00:01.123 [http-nio-8080-exec-1] INFO [4bf92f3577b34da6a3ce929d0e0e4736] - 订单服务收到请求
# 服务 B 日志(TraceId 自动透传)
2026-07-19 10:00:01.456 [http-nio-8081-exec-1] INFO [4bf92f3577b34da6a3ce929d0e0e4736] - 支付服务处理请求
5.3 手动透传(非 HTTP 场景)
如果你的服务间通过消息队列(Kafka/RabbitMQ)或自定义协议通信,需要手动实现上下文注入与提取。
客户端注入:
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapSetter;
// 1. 获取当前上下文
Context currentContext = Context.current();
// 2. 定义 Setter:将上下文写入消息 Header
TextMapSetter<Map<String, String>> setter = (carrier, key, value) ->
carrier.put(key, value);
// 3. 注入上下文
GlobalOpenTelemetry.getPropagators().getTextMapPropagator()
.inject(currentContext, message.getHeaders(), setter);
// 4. 发送消息到 Kafka/RabbitMQ
producer.send(message);
服务端提取:
import io.opentelemetry.context.propagation.TextMapGetter;
// 1. 定义 Getter:从消息 Header 读取上下文
TextMapGetter<Map<String, String>> getter = new TextMapGetter<>() {
@Override
public Iterable<String> keys(Map<String, String> carrier) {
return carrier.keySet();
}
@Override
public String get(Map<String, String> carrier, String key) {
return carrier.get(key);
}
};
// 2. 提取上下文
Context extractedContext = GlobalOpenTelemetry.getPropagators()
.getTextMapPropagator()
.extract(Context.current(), message.getHeaders(), getter);
// 3. 在提取的上下文中创建 Span
try (Scope scope = extractedContext.makeCurrent()) {
// 业务处理——新 Span 自动成为父 Span 的子级
processMessage(message);
}
六、在 Jaeger 中分析调用链
6.1 查找 Trace
访问 Jaeger UI(http://localhost:16686),在 Service 下拉框中选择你的服务(如 order-service),点击 Find Traces。
6.2 解读火焰图
Jaeger 的 Trace 详情页展示了一个时序火焰图:
- 横轴:时间(从左到右)
- 每一行:一个 Span(操作)
- 层级关系:父 Span 在上,子 Span 在下缩进
- 颜色:不同服务用不同颜色区分
快速定位慢接口:找到最宽的 Span——那就是耗时最长的操作。
6.3 关键字段解读
| 字段 | 含义 | 排查价值 |
|---|---|---|
http.method | HTTP 方法 | 识别接口 |
http.status_code | 状态码 | 发现 500/404 异常 |
http.url | 请求路径 | 定位具体 API |
db.statement | SQL 语句 | 发现慢 SQL |
error | 是否异常 | 快速定位报错 Span |
otel.status_code | Span 状态 | OK / ERROR |
七、与现有监控体系集成
7.1 与 Prometheus + Grafana 集成
OpenTelemetry Java Agent 不仅采集 Traces,还能采集 Metrics。你可以通过配置将 Metrics 导出到 Prometheus:
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.metrics.exporter=otlp \
-Dotel.exporter.otlp.metrics.endpoint=http://otel-collector:4318/v1/metrics \
-jar app.jar
或者通过 OpenTelemetry Collector 将 Metrics 转换为 Prometheus 格式。
延伸阅读:本站的 《Java 应用接入 Prometheus + Grafana 全记录》 详细介绍了 Metrics 监控体系的搭建。将 Traces(Jaeger)+ Metrics(Prometheus)+ Logs(Loki/ELK)三者结合,就构成了完整的 可观测性铁三角。
7.2 与日志系统联动
在日志中打印 TraceId,实现 Traces ↔ Logs 的联动。OpenTelemetry Java Agent 支持自动将 TraceId 注入 MDC:
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.instrumentation.logback-mdc.enabled=true \
-jar app.jar
配合 Logback 配置:
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{trace_id}] %logger{36} - %msg%n</pattern>
在 Jaeger 中找到 TraceId 后,直接去日志系统搜索同一个 TraceId,就能看到完整的调用链 + 详细日志。
八、采样率调优:控制成本与性能
全量采集所有请求的 Trace 数据,在大流量场景下成本极高。OpenTelemetry 支持多种采样策略:
# 基于概率的采样(采样率 10%)
-Dotel.traces.sampler=traceidratio
-Dotel.traces.sampler.arg=0.1
# 父级采样(继承上游采样决策,推荐)
-Dotel.traces.sampler=parentbased_traceidratio
-Dotel.traces.sampler.arg=0.05
# 始终采样(开发环境)
-Dotel.traces.sampler=always_on
生产环境推荐:parentbased_traceidratio,采样率 5%-10%,既能捕获足够多的链路数据,又控制了存储成本。
九、生产环境最佳实践清单
- 优先使用 Java Agent:零代码侵入,维护成本最低
- 配置服务元数据:
service.name、service.version、deployment.environment必不可少 - W3C Trace Context 是默认标准:确保跨服务透传开箱即用
- 设置合理的采样率:生产环境 5%-10%,避免存储爆炸
- Jaeger 存储选型:开发用内存,生产用 Elasticsearch/Cassandra
- 与日志联动:启用 MDC 注入,实现 Traces ↔ Logs 双向跳转
- 异常自动标记:Agent 自动在异常 Span 上标记
error=true - 定期清理数据:配置 Jaeger 的 TTL(Time-To-Live),避免存储无限增长
十、总结
从零到生产,全链路追踪的落地路径清晰可见:
| 阶段 | 目标 | 关键动作 |
|---|---|---|
| 入门 | 单服务可观测 | 挂载 Java Agent,配置 OTLP 导出到 Jaeger |
| 进阶 | 跨服务链路串联 | W3C Trace Context 自动透传(HTTP)或手动注入(MQ) |
| 生产 | 规模化可观测 | 部署 Jaeger 集群、配置采样率、集成日志与 Metrics |
OpenTelemetry + Jaeger 的组合,让分布式系统的调用链从 “黑箱”变为“透视” 。当线上出现慢接口或异常时,你不再需要逐台服务器翻日志——打开 Jaeger,找到那条 Trace,一眼看穿整个请求的来龙去脉。
📌 系列拓展阅读:
- 《Java 日志最佳实践 2026:从 SLF4J 到 ELK 全链路日志追踪》——MDC + TraceId 日志串联
- 《Java 应用接入 Prometheus + Grafana 全记录》——Metrics 监控体系
- 《从 Docker 到 K8s:Spring Boot 应用部署与自动伸缩实战》——K8s 环境下的可观测性部署
- 《Arthas 与火焰图:Java 生产环境在线诊断从入门到精通》——线上问题诊断
📚 参考文献:
- OpenTelemetry 官方文档. Java Instrumentation. https://opentelemetry.io/docs/languages/java/
- OpenTelemetry Java Instrumentation GitHub. README. https://github.com/open-telemetry/opentelemetry-java-instrumentation
- W3C Trace Context 规范. Trace Context. https://www.w3.org/TR/trace-context/
- Jaeger 官方文档. Deployment. https://www.jaegertracing.io/docs/latest/deployment/
- Alibaba Cloud. Report trace data from Java applications by using OpenTelemetry. https://www.alibabacloud.com/help/en/opentelemetry/user-guide/use-opentelemetry-to-submit-trace-data-of-java-applications
- Uptrace. OpenTelemetry Spring Boot: Java Agent, Starter, and Manual Instrumentation. https://uptrace.dev/guides/opentelemetry-spring-boot
- Alibaba Cloud. Implement cross-process context propagation with the OpenTelemetry SDK for Java. https://help.aliyun.com/en/arms/application-monitoring/use-cases/implementing-cross-process-context-propagation-with-opentelemetry-sdk-for-java









