Modern Architecture
& Coding Solutions

Java 应用全链路追踪实战:OpenTelemetry + Jaeger 从入门到生产

上一篇文章我们用 MDC + TraceId 实现了单服务内的日志串联,但在微服务架构中,一次请求可能跨越 5 个服务、10 个节点——每个服务都有自己的 TraceId,日志散落在不同机器上,你无法还原完整的调用链路。OpenTelemetry 正是为此而生。本文将基于 OpenTelemetry Java Agent(零代码侵入) ,从零搭建全链路追踪体系,配合 Jaeger 实现调用链可视化,并打通 W3C Trace Context 标准实现跨服务透传。

一、为什么需要 OpenTelemetry?

在 OpenTelemetry 出现之前,分布式追踪领域是诸侯割据的局面:

追踪系统传播格式客户端库
JaegerUber-Trace-IdJaeger Client
ZipkinB3 PropagationBrave
SkyWalkingSW6SkyWalking Agent

每个追踪系统都有自己的 Header 格式和客户端库。如果你的服务 A 用 Jaeger、服务 B 用 Zipkin,TraceId 无法跨服务传递,整个调用链就断了。

OpenTelemetry 的出现统一了这一切。它是一个 CNCF 孵化项目,提供了一套厂商无关的 API、SDK 和工具,用于采集、处理和导出遥测数据(Traces、Metrics、Logs)。它支持 W3C Trace Context 标准,让不同语言的服务、不同厂商的观测后端能够无缝协作。

二、核心概念:Trace、Span、Propagator

在开始实战之前,先理清三个核心概念:

概念定义类比
Trace一次完整请求在分布式系统中的全部处理过程一次旅行的全程记录
SpanTrace 中的一个独立操作单元(如一次 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.protocolOTLP 传输协议grpchttp/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 调用中注入和提取 traceparenttracestate Header。

5.1 自动透传(Agent 默认行为)

当服务 A(通过 RestTemplate/WebClient/OkHttp)调用服务 B 时,Agent 自动:

  1. Inject(客户端) :从当前 Span 提取 Trace 上下文,写入 HTTP 请求头
  2. 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.methodHTTP 方法识别接口
http.status_code状态码发现 500/404 异常
http.url请求路径定位具体 API
db.statementSQL 语句发现慢 SQL
error是否异常快速定位报错 Span
otel.status_codeSpan 状态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%,既能捕获足够多的链路数据,又控制了存储成本。

九、生产环境最佳实践清单

  1. 优先使用 Java Agent:零代码侵入,维护成本最低
  2. 配置服务元数据service.nameservice.versiondeployment.environment 必不可少
  3. W3C Trace Context 是默认标准:确保跨服务透传开箱即用
  4. 设置合理的采样率:生产环境 5%-10%,避免存储爆炸
  5. Jaeger 存储选型:开发用内存,生产用 Elasticsearch/Cassandra
  6. 与日志联动:启用 MDC 注入,实现 Traces ↔ Logs 双向跳转
  7. 异常自动标记:Agent 自动在异常 Span 上标记 error=true
  8. 定期清理数据:配置 Jaeger 的 TTL(Time-To-Live),避免存储无限增长

十、总结

从零到生产,全链路追踪的落地路径清晰可见:

阶段目标关键动作
入门单服务可观测挂载 Java Agent,配置 OTLP 导出到 Jaeger
进阶跨服务链路串联W3C Trace Context 自动透传(HTTP)或手动注入(MQ)
生产规模化可观测部署 Jaeger 集群、配置采样率、集成日志与 Metrics

OpenTelemetry + Jaeger 的组合,让分布式系统的调用链从 “黑箱”变为“透视” 。当线上出现慢接口或异常时,你不再需要逐台服务器翻日志——打开 Jaeger,找到那条 Trace,一眼看穿整个请求的来龙去脉。


📌 系列拓展阅读


📚 参考文献

  1. OpenTelemetry 官方文档. Java Instrumentation. https://opentelemetry.io/docs/languages/java/
  2. OpenTelemetry Java Instrumentation GitHub. README. https://github.com/open-telemetry/opentelemetry-java-instrumentation
  3. W3C Trace Context 规范. Trace Context. https://www.w3.org/TR/trace-context/
  4. Jaeger 官方文档. Deployment. https://www.jaegertracing.io/docs/latest/deployment/
  5. 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
  6. Uptrace. OpenTelemetry Spring Boot: Java Agent, Starter, and Manual Instrumentation. https://uptrace.dev/guides/opentelemetry-spring-boot
  7. 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
赞(0) 打赏
未经允许不得转载:MACS Dev Hub » Java 应用全链路追踪实战:OpenTelemetry + Jaeger 从入门到生产

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续提供更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫

微信扫一扫

登录

找回密码

注册