如何用Logbook与java.net.http.HttpClient实现跨服务日志关联?
用Logbook + java.net.http.HttpClient实现跨服务日志关联(基于correlationId)
完全可以实现这个需求,核心逻辑是通过HTTP请求头传递首个服务的correlationId,同时让Logbook在记录客户端(当前服务调用下游)的请求/响应时关联该ID,下游服务也同步配置以接收和传递这个ID。具体操作步骤如下:
1. 在当前服务中传递correlationId
首先从当前服务的日志上下文(比如Logbook维护的MDC)中获取已有的correlationId,然后在用java.net.http.HttpClient发送请求时,将这个ID放入请求头(推荐用标准的X-Correlation-ID头)。
示例代码:
// 从MDC获取已有的correlationId(Logbook默认会将其存入MDC) String correlationId = MDC.get("correlationId"); // 构建HttpClient并发送带correlationId的请求 HttpClient httpClient = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://下游服务地址/api/xxx")) .header("X-Correlation-ID", correlationId) .POST(HttpRequest.BodyPublishers.ofString("请求体内容")) .build(); HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
2. 配置Logbook记录客户端请求/响应并关联ID
默认Logbook仅记录当前服务作为HTTP服务器接收的请求,需要开启客户端日志支持,让它记录HttpClient发起的外部调用,并自动关联correlationId。
Spring Boot场景配置
在application.yml中添加以下配置:
logbook: client: enabled: true # 开启客户端请求日志 correlation: request-header: X-Correlation-ID # 指定从该请求头获取correlationId response-header: X-Correlation-ID # 响应头也返回该ID filter: enabled: true
纯Java项目配置
手动为HttpClient添加Logbook拦截器:
// 初始化Logbook实例 Logbook logbook = Logbook.create(); // 给HttpClient注册Logbook拦截器 HttpClient httpClient = HttpClient.newBuilder() .addInterceptorFirst(new LogbookHttpClientInterceptor(logbook)) .build();
配置后,Logbook会自动记录HttpClient的请求和响应,并将X-Correlation-ID作为关联ID写入日志。
3. 下游服务(第二、第三个服务)的同步配置
要让下游服务也能关联同一个correlationId,需要:
- 下游服务同样配置Logbook,开启correlationId支持,指定从
X-Correlation-ID头获取ID - 当下游服务需要调用第三个服务时,重复步骤1的逻辑:从自身MDC获取correlationId,通过
HttpClient请求头传递给第三个服务
4. 异步调用场景的MDC上下文传递
如果使用HttpClient的异步方法(sendAsync),需要手动传递MDC上下文,否则异步线程无法获取correlationId:
// 保存当前线程的MDC上下文 Map<String, String> mdcContext = MDC.getCopyOfContextMap(); HttpClient httpClient = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://下游服务地址/api/xxx")) .header("X-Correlation-ID", MDC.get("correlationId")) .build(); CompletableFuture<HttpResponse<String>> future = httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) .thenApply(response -> { // 恢复MDC上下文,确保日志关联 MDC.setContextMap(mdcContext); // 处理响应逻辑 return response; }) .whenComplete((res, ex) -> MDC.clear()); // 清理上下文
内容的提问来源于stack exchange,提问作者Jackeline Santos
相关产品推荐
相关产品推荐

