Java 语言 OpenTelemetry 接入指南
本文档提供 Java 应用接入 OpenTelemetry 的完整指南,重点介绍使用 Java Agent 进行零代码接入,以及 SDK 手动接入方式。
目录
概述
Java 语言特性
Java 语言的 OpenTelemetry 支持具有以下特点:
- 成熟稳定: Java Agent 和 SDK 已达到 1.0 稳定版本
- 零代码接入: 通过 Java Agent 实现完全零侵入
- 广泛支持: 支持 100+ 主流 Java 框架和库
- 生产就绪: 在大量生产环境中验证
- Spring Boot 集成: 官方提供 Spring Boot Starter
版本要求
- Java 版本: Java 8+ (推荐 Java 17+)
- OpenTelemetry Java: v1.30.0+
- Java Agent: v2.0.0+
- 支持架构: x86_64, ARM64
支持的信号
- ✅ Traces: 完全支持
- ✅ Metrics: 完全支持
- ✅ Logs: 完全支持(Java 9+)
适用场景
- Spring Boot 应用
- 微服务架构
- 传统 Java EE 应用
- 批处理作业
- 消息队列应用
零代码接入(推荐)
Java Agent 是 Java 零代码接入的最佳方式,通过字节码增强技术实现完全零侵入的自动埋点。
下载 Java Agent
方法 1: 从 Maven Central 下载
# 下载最新版本wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar# 或指定版本wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v2.0.0/opentelemetry-javaagent.jar
方法 2: 使用 Maven/Gradle 依赖
Maven:
<dependency><groupId>io.opentelemetry.javaagent</groupId><artifactId>opentelemetry-javaagent</artifactId><version>2.0.0</version><type>pom</type></dependency>
Gradle:
dependencies {implementation platform('io.opentelemetry.javaagent:opentelemetry-javaagent-bom:2.0.0')}
快速开始
1. 配置环境变量
# 服务信息export OTEL_SERVICE_NAME=my-java-serviceexport OTEL_RESOURCE_ATTRIBUTES=service.version=1.0.0,deployment.environment=production# OTLP Exporterexport OTEL_EXPORTER_OTLP_ENDPOINT=uptrace-grpc.yodao.cn:4317export OTEL_EXPORTER_OTLP_PROTOCOL=grpcexport OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=http://token@uptrace-grpc.yodao.cn/project# Traces 配置export OTEL_TRACES_EXPORTER=otlpexport OTEL_TRACES_SAMPLER=parentbased_traceidratioexport OTEL_TRACES_SAMPLER_ARG=0.1# Metrics 配置export OTEL_METRICS_EXPORTER=otlpexport OTEL_METRIC_EXPORT_INTERVAL=60000# Logs 配置(Java 9+)export OTEL_LOGS_EXPORTER=otlp
2. 启动应用
# 使用 -javaagent 参数java -javaagent:opentelemetry-javaagent.jar \-jar my-application.jar# 或指定 Agent 路径java -javaagent:/path/to/opentelemetry-javaagent.jar \-jar my-application.jar
就这么简单!您的应用现在已经自动埋点并开始发送遥测数据了。
支持的框架和库
Java Agent 自动支持 100+ 框架和库,包括:
Web 框架
- Spring Boot: 2.0+, 3.0+
- Spring MVC: 3.1+
- Spring WebFlux: 5.0+
- JAX-RS: Jersey, RESTEasy, Apache CXF
- Servlet: 2.3+
- Vert.x: 3.0+, 4.0+
- Quarkus: 1.0+
- Micronaut: 1.0+
数据库
- JDBC: 所有实现
- Hibernate: 3.3+
- JPA: 所有实现
- MongoDB: 3.0+, 4.0+
- Redis: Jedis, Lettuce
- Elasticsearch: 5.0+, 6.0+, 7.0+, 8.0+
- Cassandra: 3.0+, 4.0+
RPC 框架
- gRPC: 1.0+
- Apache Dubbo: 2.7+
- Apache Thrift: 0.8+
- Spring Cloud OpenFeign: 2.0+
消息队列
- Kafka: 0.11+, 1.0+, 2.0+, 3.0+
- RabbitMQ: 5.0+
- RocketMQ: 4.0+, 5.0+
- Apache Pulsar: 2.0+
- JMS: 1.1+, 2.0+
HTTP 客户端
- Apache HttpClient: 4.0+, 5.0+
- OkHttp: 3.0+, 4.0+
- Spring RestTemplate: 3.0+
- Spring WebClient: 5.0+
- Netty: 4.0+, 4.1+
其他
- Logback: 1.0+
- Log4j2: 2.0+
- SLF4J: 1.0+
- Quartz: 2.0+
- Akka: 2.5+, 2.6+
环境变量配置
必需配置
# 服务名称(必需)export OTEL_SERVICE_NAME=my-java-service# OTLP Endpoint(必需)export OTEL_EXPORTER_OTLP_ENDPOINT=uptrace-grpc.yodao.cn:4317
推荐配置
# 服务信息export OTEL_SERVICE_NAME=my-java-serviceexport OTEL_RESOURCE_ATTRIBUTES=service.version=1.0.0,deployment.environment=production,service.namespace=backend# OTLP 配置export OTEL_EXPORTER_OTLP_ENDPOINT=uptrace-grpc.yodao.cn:4317export OTEL_EXPORTER_OTLP_PROTOCOL=grpcexport OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=http://token@uptrace-grpc.yodao.cn/projectexport OTEL_EXPORTER_OTLP_COMPRESSION=gzipexport OTEL_EXPORTER_OTLP_TIMEOUT=10000# Traces 配置export OTEL_TRACES_EXPORTER=otlpexport OTEL_TRACES_SAMPLER=parentbased_traceidratioexport OTEL_TRACES_SAMPLER_ARG=0.1# Metrics 配置export OTEL_METRICS_EXPORTER=otlpexport OTEL_METRIC_EXPORT_INTERVAL=60000# Logs 配置(Java 9+)export OTEL_LOGS_EXPORTER=otlp# 日志级别export OTEL_LOG_LEVEL=info# 禁用特定埋点库(可选)export OTEL_INSTRUMENTATION_KAFKA_ENABLED=falseexport OTEL_INSTRUMENTATION_REDIS_ENABLED=true
高级配置
# 批量导出配置export OTEL_BSP_SCHEDULE_DELAY=5000export OTEL_BSP_MAX_QUEUE_SIZE=2048export OTEL_BSP_MAX_EXPORT_BATCH_SIZE=512export OTEL_BSP_EXPORT_TIMEOUT=30000# Span 限制export OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT=128export OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT=1024export OTEL_SPAN_EVENT_COUNT_LIMIT=128# Propagatorsexport OTEL_PROPAGATORS=tracecontext,baggage# 禁用 SDK(用于测试)export OTEL_SDK_DISABLED=false
启动方式
方式 1: 命令行参数
java -javaagent:opentelemetry-javaagent.jar \-jar my-application.jar
方式 2: 系统属性
java -javaagent:opentelemetry-javaagent.jar \-Dotel.service.name=my-service \-Dotel.exporter.otlp.endpoint=uptrace-grpc.yodao.cn:4317 \-Dotel.exporter.otlp.headers=uptrace-dsn=http://token@uptrace-grpc.yodao.cn/project \-jar my-application.jar
方式 3: 配置文件
创建 otel.properties 文件:
otel.service.name=my-serviceotel.exporter.otlp.endpoint=uptrace-grpc.yodao.cn:4317otel.exporter.otlp.headers=uptrace-dsn=http://token@uptrace-grpc.yodao.cn/projectotel.traces.sampler=parentbased_traceidratiootel.traces.sampler.arg=0.1
使用配置文件:
java -javaagent:opentelemetry-javaagent.jar \-Dotel.configuration.file=otel.properties \-jar my-application.jar
方式 4: Spring Boot
application.properties:
otel.service.name=my-serviceotel.exporter.otlp.endpoint=uptrace-grpc.yodao.cn:4317otel.exporter.otlp.headers=uptrace-dsn=http://token@uptrace-grpc.yodao.cn/project
启动脚本:
java -javaagent:opentelemetry-javaagent.jar \-jar my-spring-boot-app.jar
方式 5: Docker
FROM openjdk:17-jre-slim# 复制 AgentCOPY opentelemetry-javaagent.jar /app/# 复制应用COPY my-application.jar /app/# 设置环境变量ENV OTEL_SERVICE_NAME=my-serviceENV OTEL_EXPORTER_OTLP_ENDPOINT=uptrace-grpc.yodao.cn:4317ENV OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=http://token@uptrace-grpc.yodao.cn/project# 启动应用ENTRYPOINT ["java", "-javaagent:/app/opentelemetry-javaagent.jar", "-jar", "/app/my-application.jar"]
方式 6: Kubernetes
apiVersion: apps/v1kind: Deploymentmetadata:name: my-appspec:template:spec:containers:- name: appimage: my-java-app:latestenv:- name: OTEL_SERVICE_NAMEvalue: my-service- name: OTEL_EXPORTER_OTLP_ENDPOINTvalue: uptrace-grpc.yodao.cn:4317- name: OTEL_EXPORTER_OTLP_HEADERSvalueFrom:secretKeyRef:name: apm-credentialskey: otlp-headersvolumeMounts:- name: javaagentmountPath: /app/javaagent.jarsubPath: opentelemetry-javaagent.jarcommand:- java- -javaagent:/app/javaagent.jar- -jar- /app/my-application.jarvolumes:- name: javaagentconfigMap:name: javaagent---apiVersion: v1kind: ConfigMapmetadata:name: javaagentdata:opentelemetry-javaagent.jar: |# 二进制内容(从文件读取)
使用 OpenTelemetry Operator
如果使用 Kubernetes,推荐使用 OpenTelemetry Operator 自动注入 Agent。
安装 Operator
kubectl apply -f https://github.com/open-telemetry/opentelemetry-operator/releases/latest/download/opentelemetry-operator.yaml
创建 Instrumentation 资源
apiVersion: opentelemetry.io/v1alpha1kind: Instrumentationmetadata:name: my-instrumentationnamespace: defaultspec:exporter:endpoint: uptrace-grpc.yodao.cn:4317env:- name: OTEL_EXPORTER_OTLP_HEADERSvalue: uptrace-dsn=http://token@uptrace-grpc.yodao.cn/projectpropagators:- tracecontext- baggagesampler:type: parentbased_traceidratioargument: "0.1"java:image: ghcr.io/open-telemetry/opentelemetry-java-instrumentation:latest
部署应用
在 Pod 上添加 annotation:
apiVersion: apps/v1kind: Deploymentmetadata:name: my-appspec:template:metadata:annotations:instrumentation.opentelemetry.io/inject-java: "true"spec:containers:- name: appimage: my-java-app:latest
验证零代码接入
启动应用后,访问您的服务端点,然后在 APM 平台查看数据:
- Traces: 查看调用链路
- Metrics: 查看请求量、延迟等指标
- Service Map: 查看服务拓扑图
快速验证脚本
#!/bin/bash# verify-java-instrumentation.shecho "Checking service..."curl -s http://localhost:8080/actuator/health > /dev/nullecho "Waiting for traces to be exported..."sleep 10echo "Check your APM platform for service: $OTEL_SERVICE_NAME"echo "Endpoint: $OTEL_EXPORTER_OTLP_ENDPOINT"
SDK 手动接入
如果需要更细粒度的控制或自定义埋点,可以使用 SDK 手动接入。
添加依赖
Maven
<dependencies><!-- OpenTelemetry API --><dependency><groupId>io.opentelemetry</groupId><artifactId>opentelemetry-api</artifactId><version>1.30.0</version></dependency><!-- OpenTelemetry SDK --><dependency><groupId>io.opentelemetry</groupId><artifactId>opentelemetry-sdk</artifactId><version>1.30.0</version></dependency><!-- OTLP Exporter --><dependency><groupId>io.opentelemetry</groupId><artifactId>opentelemetry-exporter-otlp</artifactId><version>1.30.0</version></dependency><!-- 埋点库(可选) --><dependency><groupId>io.opentelemetry.instrumentation</groupId><artifactId>opentelemetry-spring-boot-starter</artifactId><version>2.0.0</version></dependency></dependencies>
Gradle
dependencies {// OpenTelemetry APIimplementation 'io.opentelemetry:opentelemetry-api:1.30.0'// OpenTelemetry SDKimplementation 'io.opentelemetry:opentelemetry-sdk:1.30.0'// OTLP Exporterimplementation 'io.opentelemetry:opentelemetry-exporter-otlp:1.30.0'// 埋点库(可选)implementation 'io.opentelemetry.instrumentation:opentelemetry-spring-boot-starter:2.0.0'}
初始化 OpenTelemetry
基础初始化
package com.example;import io.opentelemetry.api.OpenTelemetry;import io.opentelemetry.api.common.Attributes;import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;import io.opentelemetry.context.propagation.ContextPropagators;import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;import io.opentelemetry.sdk.OpenTelemetrySdk;import io.opentelemetry.sdk.resources.Resource;import io.opentelemetry.sdk.trace.SdkTracerProvider;import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;import io.opentelemetry.semconv.ResourceAttributes;import java.util.concurrent.TimeUnit;public class OpenTelemetryConfig {public static OpenTelemetry initializeOpenTelemetry() {// 1. 创建 ResourceResource resource = Resource.getDefault().merge(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "my-java-service",ResourceAttributes.SERVICE_VERSION, "1.0.0",ResourceAttributes.DEPLOYMENT_ENVIRONMENT, "production")));// 2. 创建 OTLP ExporterOtlpGrpcSpanExporter spanExporter = OtlpGrpcSpanExporter.builder().setEndpoint("uptrace-grpc.yodao.cn:4317").addHeader("uptrace-dsn", "http://token@uptrace-grpc.yodao.cn/project").setTimeout(10, TimeUnit.SECONDS).build();// 3. 创建 Tracer ProviderSdkTracerProvider tracerProvider = SdkTracerProvider.builder().setResource(resource).addSpanProcessor(BatchSpanProcessor.builder(spanExporter).setScheduleDelay(5, TimeUnit.SECONDS).setMaxExportBatchSize(512).setExportTimeout(30, TimeUnit.SECONDS).build()).build();// 4. 创建 OpenTelemetry 实例OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().setTracerProvider(tracerProvider).setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance())).buildAndRegisterGlobal();// 5. 注册 Shutdown HookRuntime.getRuntime().addShutdownHook(new Thread(() -> {tracerProvider.shutdown();}));return openTelemetry;}}
在 main 方法中初始化
package com.example;import io.opentelemetry.api.OpenTelemetry;public class Application {public static void main(String[] args) {// 初始化 OpenTelemetryOpenTelemetry openTelemetry = OpenTelemetryConfig.initializeOpenTelemetry();// 启动应用SpringApplication.run(Application.class, args);}}
Traces - 手动埋点
创建和使用 Span
package com.example;import io.opentelemetry.api.OpenTelemetry;import io.opentelemetry.api.common.AttributeKey;import io.opentelemetry.api.trace.Span;import io.opentelemetry.api.trace.Tracer;import io.opentelemetry.api.trace.StatusCode;import io.opentelemetry.context.Scope;public class OrderService {private static final Tracer tracer =OpenTelemetry.getGlobalTracerProvider().get("my-java-service");public void processOrder(String orderId) {// 创建 SpanSpan span = tracer.spanBuilder("process_order").setAttribute("order.id", orderId).setAttribute("operation", "process").startSpan();try (Scope scope = span.makeCurrent()) {// 添加事件span.addEvent("order_validation_started");// 验证订单validateOrder(orderId);span.addEvent("order_validated");// 处理支付processPayment(orderId);span.addEvent("payment_processed");// 设置成功状态span.setStatus(StatusCode.OK);span.setAttribute("order.success", true);} catch (Exception e) {// 记录错误span.recordException(e);span.setStatus(StatusCode.ERROR, e.getMessage());throw e;} finally {span.end();}}private void validateOrder(String orderId) {Span span = tracer.spanBuilder("validate_order").setAttribute("order.id", orderId).startSpan();try (Scope scope = span.makeCurrent()) {// 业务逻辑} finally {span.end();}}private void processPayment(String orderId) {Span span = tracer.spanBuilder("process_payment").setAttribute("order.id", orderId).setAttribute("payment.method", "credit_card").startSpan();try (Scope scope = span.makeCurrent()) {// 支付逻辑} finally {span.end();}}}
使用 @WithSpan 注解(推荐)
package com.example;import io.opentelemetry.extension.annotations.WithSpan;import org.springframework.stereotype.Service;@Servicepublic class OrderService {@WithSpan("process_order")public void processOrder(@SpanAttribute("order.id") String orderId) {validateOrder(orderId);processPayment(orderId);}@WithSpan("validate_order")private void validateOrder(@SpanAttribute("order.id") String orderId) {// 业务逻辑}@WithSpan("process_payment")private void processPayment(@SpanAttribute("order.id") String orderId) {// 支付逻辑}}
Metrics - 指标采集
Counter(计数器)
package com.example;import io.opentelemetry.api.OpenTelemetry;import io.opentelemetry.api.common.Attributes;import io.opentelemetry.api.metrics.LongCounter;import io.opentelemetry.api.metrics.Meter;public class MetricsExample {private static final Meter meter =OpenTelemetry.getGlobalMeterProvider().meterBuilder("my-java-service").build();private static final LongCounter requestCounter =meter.counterBuilder("http.server.requests").setDescription("Total number of HTTP requests").setUnit("1").build();public void handleRequest(String method, String route, int statusCode) {requestCounter.add(1, Attributes.of(AttributeKey.stringKey("http.method"), method,AttributeKey.stringKey("http.route"), route,AttributeKey.longKey("http.status_code"), statusCode));}}
Histogram(直方图)
package com.example;import io.opentelemetry.api.metrics.DoubleHistogram;public class MetricsExample {private static final DoubleHistogram requestDuration =meter.histogramBuilder("http.server.duration").setDescription("HTTP request duration").setUnit("ms").build();public void recordDuration(double durationMs, String method, String route) {requestDuration.record(durationMs, Attributes.of(AttributeKey.stringKey("http.method"), method,AttributeKey.stringKey("http.route"), route));}}
Logs - 日志集成
集成 Logback
<!-- logback-spring.xml --><configuration><appender name="OTEL" class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender"><captureExperimentalAttributes>true</captureExperimentalAttributes><captureKeyValuePairAttributes>true</captureKeyValuePairAttributes></appender><root level="INFO"><appender-ref ref="OTEL" /></root></configuration>
集成 Log4j2
<!-- log4j2.xml --><Configuration><Appenders><OpenTelemetryAppender name="OTEL"><captureExperimentalAttributes>true</captureExperimentalAttributes></OpenTelemetryAppender></Appenders><Loggers><Root level="INFO"><AppenderRef ref="OTEL" /></Root></Loggers></Configuration>
框架集成示例
Spring Boot
使用 Spring Boot Starter(推荐)
添加依赖:
<dependency><groupId>io.opentelemetry.instrumentation</groupId><artifactId>opentelemetry-spring-boot-starter</artifactId><version>2.0.0</version></dependency>
application.properties:
otel.service.name=my-spring-boot-serviceotel.exporter.otlp.endpoint=uptrace-grpc.yodao.cn:4317otel.exporter.otlp.headers=uptrace-dsn=http://token@uptrace-grpc.yodao.cn/projectotel.traces.sampler=parentbased_traceidratiootel.traces.sampler.arg=0.1
Controller 示例:
package com.example;import org.springframework.web.bind.annotation.*;import io.opentelemetry.extension.annotations.WithSpan;@RestController@RequestMapping("/api/users")public class UserController {@GetMapping("/{id}")@WithSpan("get_user")public User getUser(@PathVariable String id) {return userService.findById(id);}@PostMapping@WithSpan("create_user")public User createUser(@RequestBody User user) {return userService.save(user);}}
gRPC Server
package com.example;import io.grpc.Server;import io.grpc.ServerBuilder;import io.opentelemetry.api.OpenTelemetry;import io.opentelemetry.instrumentation.grpc.v1_6.GrpcTelemetry;public class GrpcServer {public static void main(String[] args) throws Exception {OpenTelemetry openTelemetry = OpenTelemetryConfig.initializeOpenTelemetry();GrpcTelemetry grpcTelemetry = GrpcTelemetry.create(openTelemetry);Server server = ServerBuilder.forPort(50051).intercept(grpcTelemetry.newServerInterceptor()).addService(new UserServiceImpl()).build();server.start();server.awaitTermination();}}
Kafka Consumer
package com.example;import org.apache.kafka.clients.consumer.ConsumerRecord;import org.apache.kafka.clients.consumer.ConsumerRecords;import org.apache.kafka.clients.consumer.KafkaConsumer;import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry;public class KafkaConsumerExample {public static void main(String[] args) {OpenTelemetry openTelemetry = OpenTelemetryConfig.initializeOpenTelemetry();KafkaTelemetry kafkaTelemetry = KafkaTelemetry.create(openTelemetry);Properties props = new Properties();props.put("bootstrap.servers", "localhost:9092");props.put("group.id", "my-group");KafkaConsumer<String, String> consumer =kafkaTelemetry.wrap(new KafkaConsumer<>(props));consumer.subscribe(Collections.singletonList("my-topic"));while (true) {ConsumerRecords<String, String> records = consumer.poll(100);for (ConsumerRecord<String, String> record : records) {processMessage(record);}}}}
Database - JDBC
使用 Java Agent 时,JDBC 调用会自动追踪。手动接入示例:
package com.example;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import io.opentelemetry.api.trace.Span;import io.opentelemetry.api.trace.Tracer;import io.opentelemetry.context.Scope;public class DatabaseExample {private static final Tracer tracer =OpenTelemetry.getGlobalTracerProvider().get("my-java-service");public User getUser(String id) throws Exception {Span span = tracer.spanBuilder("database.query").setAttribute("db.system", "postgresql").setAttribute("db.operation", "SELECT").startSpan();try (Scope scope = span.makeCurrent()) {Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost:5432/mydb","user", "password");PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?");stmt.setString(1, id);span.setAttribute("db.statement", "SELECT * FROM users WHERE id = ?");ResultSet rs = stmt.executeQuery();if (rs.next()) {User user = new User();user.setId(rs.getString("id"));user.setName(rs.getString("name"));span.setStatus(StatusCode.OK);return user;}span.setStatus(StatusCode.OK);return null;} catch (Exception e) {span.recordException(e);span.setStatus(StatusCode.ERROR, e.getMessage());throw e;} finally {span.end();}}}
Database - Hibernate/JPA
使用 Java Agent 时,Hibernate 调用会自动追踪。手动接入示例:
package com.example;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.stereotype.Repository;import io.opentelemetry.extension.annotations.WithSpan;@Repositorypublic interface UserRepository extends JpaRepository<User, String> {@WithSpan("find_user_by_email")User findByEmail(String email);}
HTTP Client - Apache HttpClient
package com.example;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import io.opentelemetry.instrumentation.apachehttpclient.v4_3.ApacheHttpClientTelemetry;public class HttpClientExample {public static void main(String[] args) throws Exception {OpenTelemetry openTelemetry = OpenTelemetryConfig.initializeOpenTelemetry();ApacheHttpClientTelemetry telemetry =ApacheHttpClientTelemetry.create(openTelemetry);CloseableHttpClient httpClient =telemetry.newHttpClient(HttpClients.createDefault());HttpGet request = new HttpGet("https://api.example.com/users/123");try (CloseableHttpResponse response = httpClient.execute(request)) {// 处理响应int statusCode = response.getStatusLine().getStatusCode();System.out.println("Status: " + statusCode);}}}
HTTP Client - OkHttp
package com.example;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.Response;import io.opentelemetry.instrumentation.okhttp.v3_0.OkHttpTelemetry;public class OkHttpExample {public static void main(String[] args) throws Exception {OpenTelemetry openTelemetry = OpenTelemetryConfig.initializeOpenTelemetry();OkHttpTelemetry telemetry = OkHttpTelemetry.create(openTelemetry);OkHttpClient client = telemetry.newCallFactory(new OkHttpClient());Request request = new Request.Builder().url("https://api.example.com/users/123").build();try (Response response = client.newCall(request).execute()) {// 处理响应System.out.println("Status: " + response.code());}}}
Redis - Jedis
package com.example;import redis.clients.jedis.Jedis;import io.opentelemetry.instrumentation.jedis.v4_0.JedisTelemetry;public class RedisExample {public static void main(String[] args) {OpenTelemetry openTelemetry = OpenTelemetryConfig.initializeOpenTelemetry();JedisTelemetry telemetry = JedisTelemetry.create(openTelemetry);Jedis jedis = new Jedis("localhost", 6379);Jedis instrumentedJedis = telemetry.wrap(jedis);// 自动追踪 Redis 操作instrumentedJedis.set("key", "value");String value = instrumentedJedis.get("key");System.out.println("Value: " + value);}}
Redis - Lettuce
package com.example;import io.lettuce.core.RedisClient;import io.lettuce.core.api.StatefulRedisConnection;import io.lettuce.core.api.sync.RedisCommands;import io.opentelemetry.instrumentation.lettuce.v5_1.LettuceTelemetry;public class LettuceExample {public static void main(String[] args) {OpenTelemetry openTelemetry = OpenTelemetryConfig.initializeOpenTelemetry();LettuceTelemetry telemetry = LettuceTelemetry.create(openTelemetry);RedisClient client = RedisClient.create("redis://localhost:6379");StatefulRedisConnection<String, String> connection =telemetry.wrap(client.connect());RedisCommands<String, String> commands = connection.sync();// 自动追踪 Redis 操作commands.set("key", "value");String value = commands.get("key");System.out.println("Value: " + value);}}
MongoDB
package com.example;import com.mongodb.client.MongoClient;import com.mongodb.client.MongoClients;import com.mongodb.client.MongoCollection;import com.mongodb.client.MongoDatabase;import io.opentelemetry.instrumentation.mongo.v3_1.MongoTelemetry;public class MongoExample {public static void main(String[] args) {OpenTelemetry openTelemetry = OpenTelemetryConfig.initializeOpenTelemetry();MongoTelemetry telemetry = MongoTelemetry.create(openTelemetry);MongoClient mongoClient = telemetry.wrap(MongoClients.create("mongodb://localhost:27017"));MongoDatabase database = mongoClient.getDatabase("mydb");MongoCollection<Document> collection = database.getCollection("users");// 自动追踪 MongoDB 操作Document user = collection.find(eq("id", "123")).first();System.out.println("User: " + user);}}
RabbitMQ
package com.example;import com.rabbitmq.client.Channel;import com.rabbitmq.client.Connection;import com.rabbitmq.client.ConnectionFactory;import io.opentelemetry.instrumentation.rabbitmq.v2_7.RabbitMQTelemetry;public class RabbitMQExample {public static void main(String[] args) throws Exception {OpenTelemetry openTelemetry = OpenTelemetryConfig.initializeOpenTelemetry();RabbitMQTelemetry telemetry = RabbitMQTelemetry.create(openTelemetry);ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");Connection connection = factory.newConnection();Channel channel = telemetry.wrap(connection.createChannel());// 自动追踪 RabbitMQ 操作channel.queueDeclare("my-queue", false, false, false, null);channel.basicPublish("", "my-queue", null, "Hello World".getBytes());System.out.println("Message sent");}}
WebFlux (Reactive)
package com.example;import org.springframework.web.bind.annotation.*;import org.springframework.web.reactive.function.client.WebClient;import reactor.core.publisher.Mono;import io.opentelemetry.instrumentation.spring.webflux.v5_3.SpringWebfluxTelemetry;@RestController@RequestMapping("/api")public class WebFluxController {private final WebClient webClient;public WebFluxController(WebClient.Builder webClientBuilder) {OpenTelemetry openTelemetry = OpenTelemetryConfig.initializeOpenTelemetry();SpringWebfluxTelemetry telemetry =SpringWebfluxTelemetry.create(openTelemetry);this.webClient = telemetry.wrap(webClientBuilder.build());}@GetMapping("/users/{id}")public Mono<User> getUser(@PathVariable String id) {// 自动追踪 WebFlux 调用return webClient.get().uri("https://api.example.com/users/{id}", id).retrieve().bodyToMono(User.class);}}
高级配置
自定义采样策略
import io.opentelemetry.sdk.trace.samplers.Sampler;public class CustomSampler implements Sampler {@Overridepublic SamplingResult shouldSample(Context parentContext,String traceId,String name,SpanKind spanKind,Attributes attributes,List<LinkData> parentLinks) {// 自定义采样逻辑if (name.contains("/health")) {return SamplingResult.create(Decision.DROP);}if (attributes.get(AttributeKey.stringKey("important")) != null) {return SamplingResult.create(Decision.RECORD_AND_SAMPLE);}return SamplingResult.create(Decision.RECORD_ONLY);}@Overridepublic String getDescription() {return "CustomSampler";}}
使用自定义采样器
SdkTracerProvider tracerProvider = SdkTracerProvider.builder().setSampler(new CustomSampler()).addSpanProcessor(BatchSpanProcessor.builder(spanExporter).build()).build();
多 Exporter 配置
import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter;import io.opentelemetry.exporter.logging.LoggingSpanExporter;// 创建多个 ExporterOtlpGrpcSpanExporter otlpExporter = OtlpGrpcSpanExporter.builder().setEndpoint("uptrace-grpc.yodao.cn:4317").addHeader("uptrace-dsn", "http://token@uptrace-grpc.yodao.cn/project").build();JaegerGrpcSpanExporter jaegerExporter = JaegerGrpcSpanExporter.builder().setEndpoint("http://localhost:14250").build();LoggingSpanExporter consoleExporter = LoggingSpanExporter.create();// 添加多个处理器SdkTracerProvider tracerProvider = SdkTracerProvider.builder().addSpanProcessor(BatchSpanProcessor.builder(otlpExporter).build()).addSpanProcessor(BatchSpanProcessor.builder(jaegerExporter).build()).addSpanProcessor(BatchSpanProcessor.builder(consoleExporter).build()).build();
性能优化配置
SdkTracerProvider tracerProvider = SdkTracerProvider.builder().addSpanProcessor(BatchSpanProcessor.builder(spanExporter).setMaxExportBatchSize(512) // 批量大小.setScheduleDelay(5, TimeUnit.SECONDS) // 批量延迟.setExportTimeout(30, TimeUnit.SECONDS) // 导出超时.setMaxQueueSize(2048) // 队列大小.build()).setSampler(Sampler.parentBased(Sampler.traceIdRatioBased(0.1) // 10% 采样)).build();
资源检测
Resource resource = Resource.getDefault().merge(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "my-service",ResourceAttributes.SERVICE_VERSION, "1.0.0",ResourceAttributes.DEPLOYMENT_ENVIRONMENT, "production"))).merge(Resource.getDefault().merge(Resource.create(Attributes.empty())).merge(Resource.create(Attributes.of(ResourceAttributes.HOST_NAME, System.getenv("HOSTNAME"),ResourceAttributes.OS_TYPE, System.getProperty("os.name")))));
禁用特定埋点库
# 通过环境变量禁用export OTEL_INSTRUMENTATION_KAFKA_ENABLED=falseexport OTEL_INSTRUMENTATION_REDIS_ENABLED=falseexport OTEL_INSTRUMENTATION_JDBC_ENABLED=true
自定义属性添加
import io.opentelemetry.sdk.trace.SpanProcessor;import io.opentelemetry.sdk.trace.data.SpanData;public class AttributeAddingProcessor implements SpanProcessor {@Overridepublic void onStart(Context parentContext, ReadWriteSpan span) {span.setAttribute("custom.attribute", "value");span.setAttribute("environment", System.getenv("ENV"));}@Overridepublic boolean isStartRequired() {return true;}@Overridepublic void onEnd(ReadableSpan span) {// 处理结束的 Span}@Overridepublic boolean isEndRequired() {return false;}@Overridepublic CompletableResultCode shutdown() {return CompletableResultCode.ofSuccess();}@Overridepublic CompletableResultCode forceFlush() {return CompletableResultCode.ofSuccess();}}// 使用SdkTracerProvider tracerProvider = SdkTracerProvider.builder().addSpanProcessor(new AttributeAddingProcessor()).addSpanProcessor(BatchSpanProcessor.builder(spanExporter).build()).build();
最佳实践
1. 使用 Java Agent(推荐)
对于大多数场景,推荐使用 Java Agent 进行零代码接入:
java -javaagent:opentelemetry-javaagent.jar -jar my-app.jar
优点:
- 零代码修改
- 自动支持 100+ 框架
- 统一配置管理
- 生产就绪
2. Context 传播
确保在异步操作中正确传播 Context:
// ✅ 正确:传递 ContextCompletableFuture.supplyAsync(() -> {try (Scope scope = span.makeCurrent()) {return processOrder(orderId);}}, executor);// ❌ 错误:创建新的 ContextCompletableFuture.supplyAsync(() -> {Span newSpan = tracer.spanBuilder("operation").startSpan();// 丢失了父 Span 的关联return processOrder(orderId);}, executor);
3. 使用 try-with-resources
确保 Span 正确结束:
// ✅ 正确:使用 try-with-resourcesSpan span = tracer.spanBuilder("operation").startSpan();try (Scope scope = span.makeCurrent()) {// 业务逻辑} finally {span.end();}// ✅ 更简洁:使用 @WithSpan 注解@WithSpan("operation")public void operation() {// 业务逻辑}
4. 错误处理
// ✅ 正确:记录异常并设置状态try {processOrder(orderId);span.setStatus(StatusCode.OK);} catch (Exception e) {span.recordException(e);span.setStatus(StatusCode.ERROR, e.getMessage());throw e;}
5. 属性命名
使用语义约定:
// ✅ 正确:使用语义约定span.setAttribute(SemanticAttributes.HTTP_METHOD, "GET");span.setAttribute(SemanticAttributes.HTTP_URL, "https://api.example.com/users");span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, 200L);// ❌ 错误:使用自定义键名span.setAttribute("method", "GET");span.setAttribute("url", "https://api.example.com/users");
6. 避免过度埋点
// ✅ 正确:只在关键路径埋点@WithSpan("process_order")public void processOrder(String orderId) {validateOrder(orderId);chargePayment(orderId);}// ❌ 错误:对每个小方法都埋点@WithSpan("validate_id")private void validateId(String id) { } // 太细粒度@WithSpan("check_format")private void checkFormat(String id) { } // 太细粒度
7. 资源管理
确保在应用关闭时正确关闭 OpenTelemetry:
@PreDestroypublic void shutdown() {SdkTracerProvider tracerProvider =(SdkTracerProvider) OpenTelemetry.getGlobalTracerProvider();tracerProvider.shutdown();}
8. Spring Boot 集成
使用 Spring Boot Starter 简化配置:
<dependency><groupId>io.opentelemetry.instrumentation</groupId><artifactId>opentelemetry-spring-boot-starter</artifactId><version>2.0.0</version></dependency>
# application.propertiesotel.service.name=my-serviceotel.exporter.otlp.endpoint=uptrace-grpc.yodao.cn:4317otel.exporter.otlp.headers=uptrace-dsn=http://token@uptrace-grpc.yodao.cn/project
故障排查
常见问题
1. 数据未发送到后端
症状: APM 平台看不到数据
排查步骤:
- 检查环境变量:
echo $OTEL_SERVICE_NAMEecho $OTEL_EXPORTER_OTLP_ENDPOINTecho $OTEL_EXPORTER_OTLP_HEADERS
- 检查 Java Agent 是否正确加载:
# 启动时应该看到类似日志# [otel.javaagent] Starting OpenTelemetry Javaagent
- 启用调试日志:
export OTEL_LOG_LEVEL=debug
- 使用 Console Exporter 验证:
export OTEL_TRACES_EXPORTER=logging
- 检查采样率:
# 临时设置为 100% 采样export OTEL_TRACES_SAMPLER_ARG=1.0
解决方案:
- 确认 endpoint 地址正确
- 确认 headers 包含正确的认证信息
- 检查防火墙规则
- 验证 DSN 是否有效
2. Span 未关联
症状: Trace 中的 Span 没有父子关系
原因: Context 传递不正确
解决方案:
// ✅ 正确:使用 try-with-resources 传递 ContextSpan parentSpan = tracer.spanBuilder("parent").startSpan();try (Scope parentScope = parentSpan.makeCurrent()) {Span childSpan = tracer.spanBuilder("child").startSpan();try (Scope childScope = childSpan.makeCurrent()) {// 业务逻辑} finally {childSpan.end();}} finally {parentSpan.end();}
3. 内存泄漏
症状: 内存使用持续增长
可能原因:
- Span 未正确结束
- 队列积压
- 资源未释放
解决方案:
- 确保 Span 正确结束:
// ✅ 正确:使用 try-with-resourcesSpan span = tracer.spanBuilder("operation").startSpan();try (Scope scope = span.makeCurrent()) {// 业务逻辑} finally {span.end();}
- 调整队列大小:
export OTEL_BSP_MAX_QUEUE_SIZE=1024export OTEL_BSP_SCHEDULE_DELAY=2000
- 检查资源释放:
Runtime.getRuntime().addShutdownHook(new Thread(() -> {SdkTracerProvider tracerProvider =(SdkTracerProvider) OpenTelemetry.getGlobalTracerProvider();tracerProvider.shutdown();}));
4. 高 CPU 使用率
症状: CPU 使用率异常高
可能原因:
- 采样率过高
- 同步导出
- 属性处理过多
解决方案:
- 降低采样率:
export OTEL_TRACES_SAMPLER_ARG=0.01 # 1%
- 确保异步导出:
// ✅ 正确:使用 BatchSpanProcessorBatchSpanProcessor.builder(spanExporter).build()// ❌ 错误:使用 SimpleSpanProcessorSimpleSpanProcessor.create(spanExporter)
- 限制属性数量:
export OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT=64
5. 框架未自动埋点
症状: 某些框架的调用没有被追踪
可能原因:
- 框架版本不支持
- 埋点库未启用
- 配置不正确
解决方案:
检查框架支持:
- 查看 支持的框架列表
- 检查框架版本要求
启用埋点库:
# 检查是否被禁用echo $OTEL_INSTRUMENTATION_SPRING_WEB_ENABLED# 确保未禁用export OTEL_INSTRUMENTATION_SPRING_WEB_ENABLED=true
- 检查 Java Agent 版本:
# 确保使用最新版本java -javaagent:opentelemetry-javaagent.jar -version
6. Spring Boot 集成问题
症状: Spring Boot 应用未自动埋点
解决方案:
- 检查依赖:
<dependency><groupId>io.opentelemetry.instrumentation</groupId><artifactId>opentelemetry-spring-boot-starter</artifactId><version>2.0.0</version></dependency>
- 检查配置:
# application.propertiesotel.service.name=my-serviceotel.exporter.otlp.endpoint=uptrace-grpc.yodao.cn:4317
- 检查 Java Agent:
# 确保使用 -javaagent 参数java -javaagent:opentelemetry-javaagent.jar -jar app.jar
7. 认证失败
症状: 收到认证错误
错误信息:
authentication failed: invalid DSN
解决方案:
- 检查 DSN 格式:
echo $OTEL_EXPORTER_OTLP_HEADERS# 应该包含: uptrace-dsn=http://token@uptrace-grpc.yodao.cn/project
验证 Token 有效性:
- 联系管理员确认 Token 是否有效
- 检查 Token 是否过期
检查 Headers 格式:
# ✅ 正确格式export OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=http://token@uptrace-grpc.yodao.cn/project# ❌ 错误格式export OTEL_EXPORTER_OTLP_HEADERS=api-key=token
调试技巧
1. 启用详细日志
export OTEL_LOG_LEVEL=debugexport OTEL_JAVAAGENT_DEBUG=true
2. 使用 Console Exporter
export OTEL_TRACES_EXPORTER=loggingexport OTEL_METRICS_EXPORTER=loggingexport OTEL_LOGS_EXPORTER=logging
3. 检查 Java Agent 加载
启动应用时应该看到:
[otel.javaagent] Starting OpenTelemetry Javaagent 2.0.0[otel.javaagent] OpenTelemetry Javaagent initialized
4. 使用 JVM 参数
java -javaagent:opentelemetry-javaagent.jar \-Dotel.javaagent.debug=true \-Dotel.log.level=debug \-jar my-app.jar
5. 检查埋点库状态
# 查看启用的埋点库export OTEL_JAVAAGENT_DEBUG=true# 启动应用,查看日志中的 "Instrumenting" 信息
环境变量完整示例
# 开发环境 - 完整调试export OTEL_LOG_LEVEL=debugexport OTEL_SERVICE_NAME=my-service-devexport OTEL_TRACES_SAMPLER=always_onexport OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317export OTEL_TRACES_EXPORTER=otlp,logging# 生产环境 - 优化性能export OTEL_LOG_LEVEL=warnexport OTEL_SERVICE_NAME=my-serviceexport OTEL_RESOURCE_ATTRIBUTES=service.version=1.0.0,deployment.environment=productionexport OTEL_EXPORTER_OTLP_ENDPOINT=uptrace-grpc.yodao.cn:4317export OTEL_EXPORTER_OTLP_PROTOCOL=grpcexport OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=http://token@uptrace-grpc.yodao.cn/projectexport OTEL_EXPORTER_OTLP_COMPRESSION=gzipexport OTEL_TRACES_SAMPLER=parentbased_traceidratioexport OTEL_TRACES_SAMPLER_ARG=0.1export OTEL_BSP_MAX_QUEUE_SIZE=2048export OTEL_BSP_MAX_EXPORT_BATCH_SIZE=512export OTEL_BSP_SCHEDULE_DELAY=5000
下一步
参考资源
- OpenTelemetry Java Documentation
- OpenTelemetry Java SDK
- OpenTelemetry Java Instrumentation
- Java Agent Documentation
- Spring Boot Starter
- 支持的框架列表
- Java Semantic Conventions
- Example Applications