OpenTelemetry【一、对接 skywalking OAP】
OpenTelemetry + SpringBoot 接入 SkyWalking 完整教程
整体架构说明
SkyWalking OAP 原生支持 OpenTelemetry OTLP 协议(HTTP/gRPC)
流程:
SpringBoot(OpenTelemetry SDK) → OTLP协议 → SkyWalking OAP Server → SkyWalking UI展示链路
两种方案区分:
- OpenTelemetry Java Agent(无代码侵入,推荐)
- OpenTelemetry SDK 编码集成(代码侵入,灵活定制)
SkyWalking OAP 默认开启 OTLP 接收端口:
- OTLP gRPC:
11800(默认兼容,和SkyWalking Java Agent共用端口) - OTLP HTTP:
12800
方案一:OpenTelemetry Java Agent(零代码侵入|首选)
不需要修改一行业务代码,启动挂载agent即可采集链路,上报SkyWalking。
1. 准备文件
下载 OpenTelemetry Java Agent
地址:https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases
文件名:opentelemetry-javaagent.jar
2. SpringBoot 启动参数
java \
-javaagent:./opentelemetry-javaagent.jar \
# OTLP 上报地址,指向 SkyWalking OAP
-Dotel.exporter.otlp.endpoint=http://127.0.0.1:11800 \
-Dotel.exporter.otlp.protocol=grpc \
# 服务名称(SkyWalking UI展示的服务名)
-Dotel.service.name=otel-demo-service \
# 采样率:1.0=全量采集,生产可调整0.1
-Dotel.traces.sampler=parentbased_always_on \
# 开启MDC自动注入traceId(关键!日志打印traceId)
-Dotel.javaagent.logging.application-logging.mdc.enabled=true \
-jar demo.jar
⚠️ 重要:SkyWalking OAP 无需额外修改配置,默认接收 OTLP gRPC 数据。
3. 日志打印 TraceId(MDC方式)
OpenTelemetry Agent自动将 traceId、spanId、trace_flags 注入SLF4J MDC
MDC Key:
traceIdspanIdtrace_flags
logback.xml 日志模板
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} traceId=%X{traceId} spanId=%X{spanId} - %msg%n</pattern>
</encoder>
启动应用调用接口,日志自动输出traceId。
4. 在代码中获取 TraceId(无需引入依赖)
import org.slf4j.MDC;
String traceId = MDC.get("traceId");
如果想要API方式获取(方案二SDK模式使用)
方案二:OpenTelemetry SDK 代码集成(有侵入,适合自定义埋点)
Step1 Maven依赖
<!-- OpenTelemetry BOM统一版本管理 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-bom</artifactId>
<version>1.40.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- OpenTelemetry Core -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
</dependency>
<!-- OTLP gRPC Exporter 上报SkyWalking -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>
<!-- SpringBoot Web自动埋点工具 -->
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-spring-boot-starter</artifactId>
<version>2.11.0-alpha</version>
</dependency>
</dependencies>
Step2 application.yml 配置(OTLP上报SkyWalking)
otel:
service:
name: otel-spring-demo
traces:
exporter: otlp
exporter:
otlp:
endpoint: http://127.0.0.1:11800
protocol: grpc
instrumentation:
logging:
mdc-enabled: true # 自动MDC注入traceId
Step3 API获取TraceId & 手动创建Span
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.TracerProvider;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@RestController
public class OtelTraceController {
private static final Logger log = LoggerFactory.getLogger(OtelTraceController.class);
@Autowired
private OpenTelemetry openTelemetry;
// 获取Tracer
private Tracer getTracer() {
return openTelemetry.getTracer("otel-demo-tracer");
}
@GetMapping("/otel/test")
public String testTrace() {
// ============ 方式1:从当前上下文获取TraceId ============
Span currentSpan = Span.current();
String traceId = currentSpan.getSpanContext().getTraceId();
String spanId = currentSpan.getSpanContext().getSpanId();
log.info("接口调用 traceId={}, spanId={}", traceId, spanId);
// ============ 方式2:MDC获取(和日志保持一致) ============
// MDC.get("traceId");
// ============ 手动新建自定义Span(模拟业务埋点) ============
Tracer tracer = getTracer();
Span childSpan = tracer.spanBuilder("自定义业务-span-订单处理")
.setAttribute("orderId", "10086")
.startSpan();
try (Scope scope = childSpan.makeCurrent()) {
// span作用域内代码
doBusiness();
} catch (Exception e) {
// 上报异常到链路
childSpan.recordException(e);
throw e;
} finally {
childSpan.end(); // 必须关闭span
}
return "success, traceId:" + traceId;
}
private void doBusiness() {
}
}
Step4 关键说明:Span生命周期
Span span = tracer.spanBuilder("span名称").startSpan();
try(Scope scope = span.makeCurrent()){
// 业务代码,当前上下文切换到新span
}finally{
span.end(); // 结束span,数据发送到exporter
}
Step5 异步场景传递上下文
OpenTelemetry 使用 Context.wrap() 包装线程任务传递链路上下文
Runnable task = Context.current().wrap(() -> {
Span span = Span.current();
log.info("异步线程traceId:{}", span.getSpanContext().getTraceId());
});
new Thread(task).start();
三、SkyWalking OAP 关键配置校验(非常重要)
SkyWalking 默认开启OTLP接收,无需额外修改。
文件:application.yml(oap配置)
receiver-otlp:
selector: ${SW_RECEIVER_OTLP:default}
default:
# gRPC端口默认11800
grpc:
host: 0.0.0.0
port: 11800
✅ OpenTelemetry OTLP gRPC 和 SkyWalking Java Agent 使用同一个11800端口,可以混合接入
即:一部分服务使用skywalking-agent,一部分使用opentelemetry,链路互通,UI统一查看。
协议选择建议
grpc性能更好,推荐otel.exporter.otlp.protocol=grpc- 如果网络限制只能http:
protocol=http/protobuf,端口改为12800
四、常见问题汇总
1. SkyWalking UI看不到OpenTelemetry上报的数据
- 确认endpoint地址是 OAP地址:11800,不要填UI的8081
- 检查OAP启动日志:
receiver-otlp gRPC server start - 应用启动日志看OTLP exporter有没有报错连接失败
- 必须发起接口请求产生链路,无调用无数据
2. MDC拿不到traceId
- Agent模式:添加启动参数
-Dotel.javaagent.logging.application-logging.mdc.enabled=true - SDK starter模式:
otel.instrumentation.logging.mdc-enabled=true
3. TraceId为空/无效
Span.current().getSpanContext().isValid() 先判断有效性;
只有在产生链路的请求线程内才能获取有效traceId;单元测试、定时任务若无埋点则为空。
4. OpenTelemetry 和 SkyWalking Agent能不能混用?
✅ 可以!同一个OAP同时接收两种协议
- SkyWalking Agent:SkyWalking原生协议
- OpenTelemetry SDK/Agent:OTLP协议
最终全部在SkyWalking UI统一展示拓扑、链路。
⚠️ 禁止同一个进程同时挂载 skywalking-agent + opentelemetry-agent,两者会冲突字节码增强!
五、OpenTelemetry vs SkyWalking Agent 选型对比
| 方案 | 侵入性 | 协议 | 适用场景 |
|---|---|---|---|
| SkyWalking Java Agent | 零代码 | SkyWalking自定义协议 | 纯Java栈,不想迁移标准OTel |
| OpenTelemetry Java Agent | 零代码 | OTLP | 多语言技术栈、未来兼容多种可观测平台 |
| OpenTelemetry SDK编码 | 代码侵入 | OTLP | 需要高度自定义埋点、特殊链路处理 |
本文来自博客园,作者:蓝迷梦,转载请注明原文链接:https://www.cnblogs.com/hewei-blogs/articles/22171206

浙公网安备 33010602011771号