Spring Boot 3.2.0升级后OpenTelemetry可观测性栈抛出BadRequest异常
问题背景
升级Spring Boot到3.2.0版本,搭配OpenTelemetry Java SDK做可观测性追踪时,出现Zipkin导出Span失败的400错误,提示valid annotation timestamp required。该问题仅在引入datasource-micrometer-spring-boot依赖时触发,Spring Boot 3.1.3版本无此问题。
核心原因
datasource-micrometer-spring-boot依赖在生成数据库操作的Span注解(事件)时,未正确设置有效时间戳,导致Zipkin服务器接收到包含无效时间戳的Span数据,从而返回400错误。Spring Boot 3.2.0对Micrometer Tracing与OpenTelemetry的集成逻辑做了调整,和该依赖的兼容性出现冲突。
解决方案
1. 等待官方修复
该问题已提交至对应项目Issue,可关注项目版本更新,待修复版本发布后直接升级依赖即可。
2. 临时禁用数据源观测(快速 workaround)
如果暂时不需要数据库操作的追踪观测,可以直接关闭数据源的观测功能,避免生成无效的Span数据:
management: observations: datasource: enabled: false
3. 自定义Span过滤逻辑(保留观测的前提下修复错误)
通过包装ZipkinSpanExporter,过滤掉包含无效时间戳的Span事件,确保导出到Zipkin的Span数据都是合法的:
import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.export.SpanExporter; import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; import io.opentelemetry.sdk.common.CompletableResultCode; import org.springframework.stereotype.Component; @Component public class ValidatingZipkinSpanExporter implements SpanExporter { private final ZipkinSpanExporter delegate; public ValidatingZipkinSpanExporter(ZipkinSpanExporter delegate) { this.delegate = delegate; } @Override public CompletableResultCode export(Collection<SpanData> spans) { Collection<SpanData> filteredSpans = spans.stream() .map(this::removeInvalidTimestampEvents) .collect(Collectors.toList()); return delegate.export(filteredSpans); } private SpanData removeInvalidTimestampEvents(SpanData span) { List<SpanData.Event> validEvents = span.getEvents().stream() // 过滤掉时间戳为0或负数的无效事件 .filter(event -> event.getEpochNanos() > 0) .collect(Collectors.toList()); return SpanData.newBuilder(span) .setEvents(validEvents) .build(); } @Override public CompletableResultCode flush() { return delegate.flush(); } @Override public void shutdown() { delegate.shutdown(); } }
注册该自定义Exporter后,会自动过滤掉无效的Span事件,避免Zipkin返回错误。
4. 替换数据源观测实现
暂时替换datasource-micrometer-spring-boot,改用OpenTelemetry官方的JDBC自动 instrumentation依赖,实现数据库操作的追踪:
<dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-instrumentation-jdbc</artifactId> <scope>runtime</scope> </dependency>
这种方式可以绕过第三方依赖的兼容性问题,直接使用OpenTelemetry原生的观测能力。
内容的提问来源于stack exchange,提问作者kcsurapaneni

