You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot 3集成OpenTelemetry Logback Appender后TraceID丢失问题

问题:Spring Boot 3集成OpenTelemetry日志导出后traceID/spanID/requestId丢失

在Spring Boot 3 + Tracing环境中,通过自定义SdkLoggerProvider手动实现OpenTelemetry日志导出,添加官方推荐的Logback Appender后,日志中的traceID、spanID、requestId等字段全部为空。

日志对比

未添加Logback Appender时的日志

2023-12-05T22:36:06.055Z DEBUG [payment-service,traceID=30855c52a5c6025aa466bd16a2ec9791,7cf1b551d2c66da6,requestId=c6e2286e-bc42-4b84-8c63-f33604986ddb] 36480 --- [payment-service] [nio-8080-exec-2] [30855c52a5c6025aa466bd16a2ec9791-7cf1b551d2c66da6] o.s.web.servlet.DispatcherServlet        : GET "/v1/payments?usernameFrom=admina", parameters={masked}
2023-12-05T22:36:06.061Z DEBUG [payment-service,traceID=30855c52a5c6025aa466bd16a2ec9791,7cf1b551d2c66da6,requestId=c6e2286e-bc42-4b84-8c63-f33604986ddb] 36480 --- [payment-service] [nio-8080-exec-2] [30855c52a5c6025aa466bd16a2ec9791-7cf1b551d2c66da6] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.springboot.SimpleRestApplication$PaymentController#getAllByUsernameFrom(String)
2023-12-05T22:36:06.079Z  INFO [payment-service,traceID=30855c52a5c6025aa466bd16a2ec9791,7cf1b551d2c66da6,requestId=c6e2286e-bc42-4b84-8c63-f33604986ddb] 36480 --- [payment-service] [nio-8080-exec-2] [30855c52a5c6025aa466bd16a2ec9791-7cf1b551d2c66da6] c.e.springboot.SimpleRestApplication     : Return list of payments
2023-12-05T22:36:06.095Z DEBUG [payment-service,traceID=30855c52a5c6025aa466bd16a2ec9791,7cf1b551d2c66da6,requestId=c6e2286e-bc42-4b84-8c63-f33604986ddb] 36480 --- [payment-service] [nio-8080-exec-2] [30855c52a5c6025aa466bd16a2ec9791-7cf1b551d2c66da6] org.hibernate.SQL                        : select u1_0.username,u1_0.active,u1_0.balance,u1_0.locale from tb_user u1_0 where u1_0.username=?
Hibernate: select u1_0.username,u1_0.active,u1_0.balance,u1_0.locale from tb_user u1_0 where u1_0.username=?

添加Logback Appender后的日志

2023-12-05T22:41:02.781Z DEBUG [payment-service,traceID=,,requestId=] 37024 --- [payment-service] [nio-8080-exec-3] [                                                 ] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.springboot.SimpleRestApplication$PaymentController#getAllByUsernameFrom(String)
2023-12-05T22:41:02.783Z  INFO [payment-service,traceID=,,requestId=] 37024 --- [payment-service] [nio-8080-exec-3] [                                                 ] c.e.springboot.SimpleRestApplication     : Return list of payments
2023-12-05T22:41:02.792Z DEBUG [payment-service,traceID=,,requestId=] 37024 --- [payment-service] [nio-8080-exec-3] [                                                 ] org.hibernate.SQL                        : select u1_0.username,u1_0.active,u1_0.balance,u1_0.locale from tb_user u1_0 where u1_0.username=?
Hibernate: select u1_0.username,u1_0.active,u1_0.balance,u1_0.locale from tb_user u1_0 where u1_0.username=?

现有代码及配置

Spring Boot主类代码

public static void main(String[] args) {
    new SpringApplicationBuilder(SimpleRestApplication.class)
            .initializers((ApplicationContextInitializer<GenericApplicationContext>) applicationContext -> applicationContext.registerBean(SdkLoggerProvider.class, () -> {
                ConfigurableEnvironment environment = applicationContext.getEnvironment();

                String endpoint = environment.getProperty("otel.exporter.logs.endpoint", "http://localhost:4317");

                return SdkLoggerProvider.builder()
                        .addLogRecordProcessor(
                                BatchLogRecordProcessor.builder(OtlpGrpcLogRecordExporter.builder().setEndpoint(endpoint).build()).build())
                        .build();
            }))
            .run(args);
}

application.yml配置

spring:
  application:
    name: payment-service
management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus
  tracing:
    propagation:
      produce: w3c, b3, b3_multi
      consume: b3, w3c, b3_multi
    baggage:
      remote-fields: requestId
      correlation:
        fields: requestId
    sampling:
      probability: 1.0
  otlp:
    metrics:
      export:
        url: ${OTLP_URL:http://localhost:4318}/v1/metrics
    tracing:
      endpoint: ${OTLP_URL:http://localhost:4318}/v1/traces
  opentelemetry:
    resource-attributes:
      "service.name": ${spring.application.name}
logging:
  pattern:
    level: "%5p [${spring.application.name:},traceID=%X{traceId:-},%X{spanId:-},requestId=%X{requestId:-}]"

logback-spring.xml配置

<appender name="OpenTelemetry"
          class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender">
</appender>

<root level="info">
    <appender-ref ref="OpenTelemetry"/>
    <appender-ref ref="CONSOLE" />
</root>

解决方案

问题原因

自定义的SdkLoggerProvider未关联Spring Boot自动配置的OpenTelemetry上下文,导致日志Appender无法获取当前线程的trace/span信息;同时Logback Appender默认自动获取全局SdkLoggerProvider,但自定义Bean的初始化顺序和关联逻辑存在问题。

具体修改步骤

1. 调整SdkLoggerProvider初始化逻辑,关联Spring的OpenTelemetry实例

修改主类代码,使用Spring容器中已配置好的OpenTelemetry实例来构建SdkLoggerProvider,确保上下文传播正确:

public static void main(String[] args) {
    new SpringApplicationBuilder(SimpleRestApplication.class)
            .initializers((ApplicationContextInitializer<GenericApplicationContext>) applicationContext -> {
                // 获取Spring自动配置的OpenTelemetry实例
                OpenTelemetry openTelemetry = applicationContext.getBean(OpenTelemetry.class);
                ConfigurableEnvironment environment = applicationContext.getEnvironment();

                String endpoint = environment.getProperty("otel.exporter.logs.endpoint", "http://localhost:4317");

                SdkLoggerProvider loggerProvider = SdkLoggerProvider.builder()
                        .setResource(openTelemetry.getSdkTracerProvider().getResource())
                        .addLogRecordProcessor(
                                BatchLogRecordProcessor.builder(OtlpGrpcLogRecordExporter.builder().setEndpoint(endpoint).build()).build())
                        // 绑定上下文传播器,确保能获取trace/span信息
                        .setContextPropagator(openTelemetry.getPropagators().getTextMapPropagator())
                        .build();

                applicationContext.registerBean(SdkLoggerProvider.class, () -> loggerProvider);
            })
            .run(args);
}

2. 配置Logback Appender绑定自定义SdkLoggerProvider

在logback-spring.xml中为OpenTelemetryAppender指定Spring容器中的Bean名称,确保Appender使用正确的日志提供者:

<appender name="OpenTelemetry"
          class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender">
    <!-- 指定Spring容器中SdkLoggerProvider的Bean名称 -->
    <loggerProviderBeanName>sdkLoggerProvider</loggerProviderBeanName>
</appender>

<root level="info">
    <appender-ref ref="OpenTelemetry"/>
    <appender-ref ref="CONSOLE" />
</root>

3. 自定义日志处理器注入requestId字段

添加自定义LogRecordProcessor,将MDC中的requestId字段注入到OpenTelemetry日志属性中,确保导出的日志包含该字段:

SdkLoggerProvider loggerProvider = SdkLoggerProvider.builder()
        .setResource(openTelemetry.getSdkTracerProvider().getResource())
        .addLogRecordProcessor(
                BatchLogRecordProcessor.builder(OtlpGrpcLogRecordExporter.builder().setEndpoint(endpoint).build()).build())
        .setContextPropagator(openTelemetry.getPropagators().getTextMapPropagator())
        // 注入requestId到日志属性
        .addLogRecordProcessor(new LogRecordProcessor() {
            @Override
            public void onEmit(LogRecord logRecord) {
                String requestId = MDC.get("requestId");
                if (requestId != null) {
                    logRecord.setAttribute("requestId", AttributeValue.stringValue(requestId));
                }
            }

            @Override
            public void shutdown() {}

            @Override
            public void forceFlush() {}
        })
        .build();

4. 验证效果

启动应用后,控制台日志中的traceID、spanID、requestId会恢复正常,同时日志会正确导出到指定的OTLP端点。


内容的提问来源于stack exchange,提问作者user2669657

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 12:14:55