我尝试在微服务调用之间传播跟踪 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;
}
即使您使用 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);
}
}