Spring Boot 4 的 OpenTelemetry 自动配置边界
目录
Spring Boot 4 带了一个官方的 spring-boot-starter-opentelemetry(Maven Central 上从 4.0.0 就有,截至 2026-09-17 的正式版是 4.1.1)。它自动配好的是 OpenTelemetry SDK 和 OTLP 日志 exporter。
这篇说的「四个信号」是 traces、metrics、logs 加 profiles。OpenTelemetry 把这类数据统称 signal,官方文档 Signals 那一页列出来的四个是 traces、metrics、logs、baggage,其中 baggage 是随请求带下去的业务上下文,系列里 tracing 那篇单独讲过它;profiles 至今还在开发中。四类在 4.1 上各自停在哪:HTTP 层的 metrics 和 span 加上依赖就有,业务指标要自己埋;logs 的导出端自动配好了,Logback 那侧的 appender 仍要手工装;profiles 在应用代码里一行都没有,代价挪到了主机上。
四个信号都接齐,为的是能互相跳:从一条指标进到对应的 trace,从 span 进到日志,再进到火焰图(flame graph)。这一步落在后端的数据源配置上,放在最后一节。
3.5 上怎么接、埋点怎么写,同系列的前三篇写过(文末有链接),这篇只看 4.1 把边界挪到了哪。依据是 timosalm/spring-boot-opentelemetry-lgtm 这个 demo 仓库,看的是两个 Spring Boot 4.1.0 服务和一个 grafana/otel-lgtm 容器,引用到的 pom、配置和埋点代码都出自它的 main 分支。compose 里另有 postgres、一个 Grafana 的 MCP server 和一个 eBPF profiler,前两个跟本篇话题无关,profiler 留到 profiles 那一节。四个信号在它里面的走向如下,profiles 那一条不经过 collector:
privileged, pid host"] os -->|"metrics / traces / logs"| col mbs -->|"metrics / traces / logs"| col col --> prom col --> loki col --> tempo ebpf -->|profiles| pyro prom --> graf loki --> graf tempo --> graf pyro --> graf
starter 自己管的三个自动配置#
跟 observability 有关的运行时依赖,order-service 的 pom 里一共三条,这一节先看前两条,第三条留到 logs 那一节(还有一条 spring-boot-starter-opentelemetry-test 只在 test scope,不算进来):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-opentelemetry</artifactId>
</dependency>
starter 带的自动配置(auto-configuration)类在文档附录里数得出来,4.1.1 是三个:OpenTelemetrySdkAutoConfiguration、OpenTelemetryLoggingAutoConfiguration、OtlpLoggingAutoConfiguration,落在 SDK 装配和日志导出这一段。tracing 与 metrics 两侧的自动配置仍在 micrometer 的模块下,各自有独立的清单页(spring-boot-micrometer-tracing-opentelemetry、spring-boot-micrometer-metrics)。
这条边界在配置键名上也能看出来。demo 的 compose 文件里,两个服务的三个 export endpoint 分属两个体系:
MANAGEMENT_OPENTELEMETRY_TRACING_EXPORT_OTLP_ENDPOINT: http://lgtm:4318/v1/traces
MANAGEMENT_OPENTELEMETRY_LOGGING_EXPORT_OTLP_ENDPOINT: http://lgtm:4318/v1/logs
MANAGEMENT_OTLP_METRICS_EXPORT_URL: http://lgtm:4318/v1/metrics
traces 和 logs 在 management.opentelemetry.* 下,metrics 走的还是 Micrometer OTLP registry 那套 management.otlp.metrics.export.url。三个端点最后打进同一个 4318 端口,配置项分在两处。
metrics 和 traces 自动埋到哪一层#
加上 actuator 和 starter,HTTP 那一层就齐了:http.server.requests、http.client.requests 这类指标和进出请求的 span 由自动配置埋好,两个服务之间那次 RestClient 调用也串在同一条 trace 上。边界停在业务语义上,订单数、订单金额、每个 SKU 卖了多少件要自己埋,用的还是 @Observed、编程式 Observation、把 MeterRegistry 包进 aspect 那几种写法,跟 3.5 上是同一套 Micrometer API,系列里埋点那篇展开过,这里不重讲。
自动配置不管的另一半是行为参数,application.yaml 的 management 块(摘 order-service):
management:
metrics:
tags:
application: ${spring.application.name}
distribution.percentiles-histogram:
http.server.requests: true
http.client.requests: true
tracing.sampling.probability: 1.0
otlp.metrics.export.step: 5s
observations.annotations.enabled: true
observations.annotations.enabled 是 @Observed / @Timed 这类注解的总开关,不打开注解不生效。metrics.tags.application 给所有指标挂上同一个标签,仓库里这行后面的注释写的是拿它跟 logs、traces 做关联。distribution.percentiles-histogram 那两行把自动埋的 HTTP 指标改成直方图导出,后端才算得出分位数,最后一节 Tempo 往 Prometheus 跳的那个查询用的就是它。采样率 1.0 和 5 秒导出间隔是 demo 的取值,生产上通常要往下压。
logs 仍要自己装 appender#
这一段是 4.x 上变化最小的地方:自动配置管到 SDK 和 OTLP 日志 exporter 为止,Logback 的 appender 不在里面。3.5 那篇里手写的那段桥接,到 4.1.1 一步没少,仍是加依赖、写一份 logback 配置把 OpenTelemetryAppender 挂到 root 上、再把容器里的 OpenTelemetry 实例交给它。系列里的 tracing 那篇贴过一份完整的 logback 配置,跟这个 demo 不完全一样:那边 include 的是 defaults.xml 和 console-appender.xml,还开了 captureExperimentalAttributes、captureKeyValuePairAttributes 两个开关,demo 这份 include 的是 base.xml,两个开关都没开。下面只记 4.x 上不一样的地方。
上面这三步之外,4.x 上依赖那侧还要多对齐一个 BOM。appender 本身是 io.opentelemetry.instrumentation:opentelemetry-logback-appender-1.0:2.29.0-alpha,它会带进一个 Spring Boot BOM 不管的 opentelemetry-api-incubator,dependencyManagement 里要补一行把版本对齐(注释是仓库原文):
<!-- The logback appender pulls in opentelemetry-api-incubator, which Spring Boot's
BOM does not manage. Align it with the managed OpenTelemetry version. -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-bom-alpha</artifactId>
<version>${opentelemetry.version}-alpha</version>
<type>pom</type>
<scope>import</scope>
</dependency>
把实例交给 appender 这一步,demo 写成一个 InitializingBean:
@Configuration(proxyBeanMethods = false)
public class OpenTelemetryConfiguration {
@Bean
InitializingBean installOpenTelemetryAppender(OpenTelemetry openTelemetry) {
return () -> OpenTelemetryAppender.install(openTelemetry);
}
}
artifact 版本号上的 -alpha,和 demo 的 logback 配置里那句 this appender component is not stable yet,说的是同一件事。
profiles 不在应用里#
OTel 的 profiles 信号截至 2026-09-17 仍标着 Status: Alpha。它回答的是哪段代码在吃资源:trace 里一个慢 span 指到方法为止,方法里究竟哪几行在花时间,得靠采样。
demo 里 profiles 这一条跟应用代码没有关系。两个服务的 pom 和配置里都没有 profiling 相关的东西,采样交给一个独立容器:
otel-ebpf-profiler:
image: otel/opentelemetry-collector-ebpf-profiler:latest
command:
- --config=/etc/ebpf-profiler-config.yaml
- --feature-gates=service.profilesSupport
hostname: ebpf-profiler
privileged: true
pid: "host"
volumes:
- ./grafana/otel-collector-config-profiling.yml:/etc/ebpf-profiler-config.yaml:ro
- /sys/kernel/debug:/sys/kernel/debug:ro
- /sys/fs/cgroup:/sys/fs/cgroup:ro
它的配置文件短到只有一个 profiles 管道,97Hz 采样,OTLP gRPC 直接打到 lgtm:4040,compose 里这个端口的注释写的是 Pyroscope:
receivers:
profiling:
samples_per_second: 97
exporters:
otlp_grpc:
endpoint: lgtm:4040
tls:
insecure: true
service:
pipelines:
profiles:
receivers: [profiling]
exporters: [otlp_grpc]
应用侧一行不改的代价,是 privileged: true、主机 pid namespace、两个 /sys 挂载,外加一个还没转正的 feature gate service.profilesSupport。它对应用侧也有一处牵连:order-service 的 pom 里 datasource-micrometer-spring-boot 被整段注释掉,理由写的是目前和这个 eBPF profiler 合不来,TODO 再看。
采回来的样本跟具体某条 trace 怎么对上,不在应用里解决。eBPF 采的是整台主机上的进程,落到某一次请求上要靠下一节的 traceToProfiles。
让四个信号能互相跳#
应用侧为这件事要做的只有一样,让指标带上 exemplar。Spring Boot 4.1 的 metrics 文档给的条件是:容器里要有一个 ExemplarContextProvider bean,用 Micrometer Tracing 的话它自动配好;默认只有被采样的 trace 会进 exemplar,management.tracing.exemplars.include 能调这个行为。Prometheus 端点那侧换成 SpanContext bean,且 all 这个取值在 Prometheus 上不支持。
剩下的都在后端。demo 的 grafana/datasources.yaml 把五处跳转都写好了:
- Prometheus 的
exemplarTraceIdDestinations让指标上的trace_id指向 Tempo; - Loki 的
derivedFields把日志里的trace_id变成一个到 Tempo 的链接; - Tempo 的
tracesToMetrics用service.name对 Prometheus 的service_name标签跳回去,查询是http_server_requests_milliseconds_bucket上的histogram_quantile,也就是前面那两行直方图开关的用处; - Tempo 的
tracesToLogsV2往 Loki 跳,用的是 homelab 那套里同一个键,取值不同:那边连 spanID 一起过滤,还挂了 k8s 的 tag; - Tempo 的
traceToProfiles指向 Pyroscope,profile 类型写死为process_cpu:cpu:nanoseconds:cpu:nanoseconds。样本跟这一次请求怎么对上,靠的是query: 'method="${__span.tags.method}"'这一行,拿当前 span 的methodtag 去匹配样本。
小结#
- 边界从依赖上就看得见:starter 管 SDK 和日志 exporter,traces、metrics 的自动配置在 micrometer 那边,配置键名跟着分两半。
- 应用代码里剩下的活是业务埋点和 Logback appender 那三步,前者跟 3.5 上是同一套 Micrometer API。
- profiles 整段在应用之外,代价是主机侧的 privileged 容器和 pid namespace,OTel 那边还是 Alpha,进生产前值得再等一等。
- 四信号互相跳靠统一标签加后端数据源配置,metrics 到 traces 那一跳另需指标带 exemplar。
相关文章#
这四篇都是 Spring Boot 应用接 OpenTelemetry。(一)(二)在同一个 Spring Boot 3.5 项目上,从整体接入到两个信号各自的细节,(三)是从那个 demo 提炼出来的一份 tracing 记录;(四)换到 Spring Boot 4.1,看官方 starter 接管了哪些活、哪些还留给应用。
- Spring Boot 3.5 接 OpenTelemetry:尽量少写手动接入,把四个信号串起来(2026) — 整体接入:自动配置能覆盖到哪、Agent 与 agentless 的取舍、Collector 与采样策略,profiles 那一格用的是 JFR
- Spring Boot 应用的 Metrics 埋点:自动埋点覆盖了什么,什么时候要自己埋(2026) — 埋点写法:
@Observed、手动 Observation API、高低基数字段的边界与命名规范 - Spring Boot 3.5 Tracing:接完之后,PII 和采样怎么管 — tracing 接完之后的事:常见组件接入、上下文传播边界、PII 处理、Logback appender 的完整配置
- Spring Boot 4 的 OpenTelemetry 自动配置边界(本篇)
参考资料#
- timosalm/spring-boot-opentelemetry-lgtm — 本篇引用的 pom、compose、配置和埋点代码都出自这个仓库的 main 分支
- spring-boot-starter-opentelemetry on Maven Central — 4.0.0 起有,写这篇时(2026-09-17)最新正式版 4.1.1
- Spring Boot: Auto-configuration Classes (spring-boot-opentelemetry) — starter 自带的三个自动配置类
- Spring Boot Reference: Metrics — exemplar 的两个 bean 和
management.tracing.exemplars.include - OpenTelemetry Docs: Signals — signal 这个叫法和它列出的四类数据
- OpenTelemetry Docs: Profiles — profiles 信号的稳定性状态