Spring Boot 3 中的 TraceId 不会传播

问题描述 投票:0回答:1

我尝试在微服务调用之间传播跟踪 ID。即使我使用 RestTemplateBuilder 并在类路径中有执行器和跟踪桥,RestTemplate 似乎也没有传播 TraceId。

我在 Spring Boot 3.3.3 和 Java 17 上运行。这里是 pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.test</groupId>
    <artifactId>tracepropagation</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>tracepropagation</name>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-tracing-bridge-otel</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

这是我的 RestTemplate 配置

@Configuration
@EnableConfigurationProperties(AppProperties.class)
public class AppConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder.build();
    }
}

我使用其余模板将简单的请求转发到同一服务进行测试:

@RestController
@AllArgsConstructor
@Slf4j
public class AppController {

    private final RestTemplate restTemplate;
    private final AppProperties props;

    @GetMapping(path = "/forward")
    public String forward() {
        log.info("forward endpoint entered");
        return this.restTemplate.getForObject("http://localhost:" + props.getPort() + "/hello", String.class);
    }

    @GetMapping(path = "/hello")
    public String hello(){
        log.info("hello endpoint entered");
        return "hello";
    }
}

我编写了一个测试用例,它期望来自 /forward 端点的日志和 /hello 端点中的日志具有相同的跟踪 ID:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class TracepropagationApplicationTests {

    @LocalServerPort
    private int port;

    @Autowired
    private AppProperties props;

    @Autowired
    private TestRestTemplate restTemplate;

    @Captor
    ArgumentCaptor<ILoggingEvent> logCaptor;

    @Mock
    private Appender<ILoggingEvent> mockAppender;


    @BeforeEach
    void beforeEach(){
        final Logger root = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
        when(this.mockAppender.getName()).thenReturn("MOCK");
        root.addAppender(this.mockAppender);
        root.setLevel(Level.INFO);
        doNothing().when(mockAppender).doAppend(logCaptor.capture());
    }

    @Test
    void testTraceIdPropagation() {
        props.setPort(port);
        assertThat(restTemplate.getForObject("http://localhost:" + port + "/forward", String.class)).isEqualTo("hello");
        String firstLogTraceId = this.getLogTraceId("forward endpoint entered");
        String secondLogTraceId = this.getLogTraceId("hello endpoint entered");
        assertThat(firstLogTraceId).isEqualTo(secondLogTraceId);
    }

    private String getLogTraceId(String logMessage) {
        var traceOptional = logCaptor.getAllValues().stream().filter(e -> e.getFormattedMessage().contains(logMessage))
                .findAny()
                .map(ILoggingEvent::getMDCPropertyMap)
                .map(m -> m.get("traceId"));
        assertThat(traceOptional).isPresent();
        return traceOptional.get();
    }
}

但是,这个测试用例失败了。根据我在文档中读到的内容,如果我使用从 Spring Boot 自动装配的 RestTemplateBuilder,自动配置应该可以工作。

但它似乎没有传播跟踪 ID。

提前谢谢!

这里还有 AppProperties 类,刚刚完成:

@ConfigurationProperties(prefix = "com.test")
@Data
public class AppProperties {

    private int port;
}
java spring-boot trace propagation micrometer-tracing
1个回答
0
投票

即使您使用 RestTemplateBuilder 并且在类路径上具有跟踪依赖项,跟踪的自动配置也不会隐式地与 RestTemplate 配合使用。您需要手动添加

TracingRestTemplateInterceptor
或配置
RestTemplateCustomizer
自动添加跟踪支持。

您可以修改您的

AppConfig
类以将跟踪拦截器添加到
RestTemplate
:

@Configuration
public class AppConfig {

    @Bean
    public RestTemplateCustomizer tracingRestTemplateCustomizer(Tracer tracer) {
        return restTemplate -> {
            restTemplate.getInterceptors().add(new TracingRestTemplateInterceptor(tracer));
        };
    }

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder.build();
    }
}

如果您没有现有的拦截器,那么您可以创建一个传播跟踪信息的自定义拦截器:

public class TracingRestTemplateInterceptor implements ClientHttpRequestInterceptor {

    private final Tracer tracer;

    public TracingRestTemplateInterceptor(Tracer tracer) {
        this.tracer = tracer;
    }

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        tracer.currentSpan().context().traceId();
        // Add tracing headers from the current span
        tracer.currentSpan().inject(tracer.currentSpan().context(), request, HttpRequest::getHeaders);
        return execution.execute(request, body);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.