Spring Cloud Sleuth 如何在盒子的 Webflux ouf 中传播 MDC 上下文,以便其内容可以记录在不同的线程中?
How does Spring Cloud Sleuth propagate the MDC context in Webflux ouf of the box so that its content can be logged in different threads?
我想知道 Spring Cloud Sleuth 如何在线程之间传播 MDC 上下文,使每个线程都可以使用 MDC 参数。
我读过这篇文章 https://simonbasle.github.io/2018/02/contextual-logging-with-reactor-context-and-mdc/,它建议使用订阅者上下文在线程之间传播 MDC 状态。
我需要对某些请求中的其他参数执行类似的操作 headers,因此我创建了将其放入当前线程的 MDC 并将其存储在订阅者上下文中的方法。但是,恢复执行的下一个线程不可用。我应该使用上下文中的值在新线程中以某种方式再次调用 MDC,但是如何呢?
@Slf4j
@RequiredArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ReactiveRequestCorrelationFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
String principal = exchange.getRequest().getHeaders().getFirst("principal-header");
MDC.put("principal", principal);
return chain.filter(exchange)
.subscriberContext(Context.of("principal", principal))
.doOnTerminate(MDC::clear);
}
}
看看这个控制器映射:
@GetMapping(value = "/v1/departments", produces = MediaType.APPLICATION_JSON_VALUE)
public Flux<Department> getDepartments() {
log.info("getDepartaments");
return webClient.get().uri("http://someService:8080/api/v1/departamentosFoo").retrieve().bodyToFlux(Departments.class)
.doOnNext(dep -> log.info("found department {}", dep));
}
它生成此日志:
logging.pattern.console="%magenta([%thread]) [%X{traceId}] [%X{spanId}] [%X{principal}] - %m%n"
[boundedElastic-2] [d0a916db8be0cbf7] [d0a916db8be0cbf7] [] - getDepartments
[boundedElastic-2] [d0a916db8be0cbf7] [fb2367685db70201] [] - found department Department(id=1, nombre=dep, fechaAlta=Fri Apr 24 14:16:20 CEST 2020, staff=1, tag=, empresa=true)
[reactor-http-nio-3] [d0a916db8be0cbf7] [d0a916db8be0cbf7] [] - found department Department(id=1, nombre=dep, fechaAlta=Fri Apr 24 14:16:20 CEST 2020, staff=1, tag=, empresa=true)
请求在 reactor-http-nio 线程中启动,然后切换到 boundedElastic-2。如您所见,显示了 traceId 和 spanId 值,但没有显示我放入过滤器中的值。 Sleuth 如何设法将其上下文 (traceId spanId) 传播到其他线程中?
他们使用 Reactor Context 和 Reactor Hooks。
来自 spring-cloud-sleuth 的相关代码:link
更新:请注意对此有很多更改,请务必检查上面的最新实现link 指向旧提交。
另外几个 link 可能有用:
根据 Martin 在他的回答中提供的信息进行了一些研究,我得出如下结论:
更新过滤器:
@Slf4j
@RequiredArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE + 6)
public class ReactiveRequestCorrelationFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(exchange)
.doOnSubscribe(s -> {
String principal = exchange.getRequest().getHeaders().getFirst("principal-header");
ExtraFieldPropagation.set("principal", principal);
});
}
}
application.yml
spring:
sleuth:
log:
slf4j:
whitelisted-mdc-keys: principal
baggage-keys: principal
有了这个,我的自定义字段开始出现在日志中,Sleuth 负责设置它并从 MDC 中清除它:
[boundedElastic-2] [fadba73bf6447d02] [fadba73bf6447d02] [myprincipal] getDepartaments
您无需任何自定义即可使其正常工作。传播自定义 HTTP headers 所需要做的就是将它们告诉 Sleuth:
#The one is on by default
spring.sleuth.log.slf4j.enabled=true
spring.sleuth.propagation-keys=principal-header
spring.sleuth.log.slf4j.whitelisted-mdc-keys=principal-header
(我使用的是属性版本,但同样适用于 yaml)
然后在你的 logback 配置中使用
[%X{principal-header}]
就是这样!
N.B。我个人更愿意从 header 名称中删除 -header
位。
在请求中添加自定义header以在日志中打印并传播,请按照2.2.6.RELEASE
中的步骤进行
例如我的 httpheader 包含一个带有键 'correlationID' 的 header,我希望它打印在日志中并且还需要标记并将它传播给其他人,我不不需要更新 java 代码中的任何内容。像这样,您可以通过更新 applicaiton.yml 文件
直接访问任何 header
application.yml
spring:
application:
name: my-app-api
sleuth:
propagation:
tag:
whitelisted-keys:
- correlationID
enabled: true
baggage:
correlation-enabled: true
correlation-fields:
- correlationID
remote-fields:
- correlationID
tag-fields:
- correlationID
trace-id128: true
log:
slf4j:
enabled: true
logging:
pattern:
level: "%5p [${spring.zipkin.service.name:${spring.application.name:-}},%X{X-Span-Export:-},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{correlationID:-}]"
level:
org:
springframework: info
确保日志模式更新为新值以在日志上打印。 (请检查 logging.pattern.level 字段)
需要header
correlationID : sample-correlation-Id-123
并在日志中
2021-01-15 12:02:52.938 INFO [my-app-api,true,6001846c68912f933a714816b62e04a0,3a714816b62e04a0,sample-correlation-Id-123]
我想知道 Spring Cloud Sleuth 如何在线程之间传播 MDC 上下文,使每个线程都可以使用 MDC 参数。
我读过这篇文章 https://simonbasle.github.io/2018/02/contextual-logging-with-reactor-context-and-mdc/,它建议使用订阅者上下文在线程之间传播 MDC 状态。
我需要对某些请求中的其他参数执行类似的操作 headers,因此我创建了将其放入当前线程的 MDC 并将其存储在订阅者上下文中的方法。但是,恢复执行的下一个线程不可用。我应该使用上下文中的值在新线程中以某种方式再次调用 MDC,但是如何呢?
@Slf4j
@RequiredArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ReactiveRequestCorrelationFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
String principal = exchange.getRequest().getHeaders().getFirst("principal-header");
MDC.put("principal", principal);
return chain.filter(exchange)
.subscriberContext(Context.of("principal", principal))
.doOnTerminate(MDC::clear);
}
}
看看这个控制器映射:
@GetMapping(value = "/v1/departments", produces = MediaType.APPLICATION_JSON_VALUE)
public Flux<Department> getDepartments() {
log.info("getDepartaments");
return webClient.get().uri("http://someService:8080/api/v1/departamentosFoo").retrieve().bodyToFlux(Departments.class)
.doOnNext(dep -> log.info("found department {}", dep));
}
它生成此日志:
logging.pattern.console="%magenta([%thread]) [%X{traceId}] [%X{spanId}] [%X{principal}] - %m%n"
[boundedElastic-2] [d0a916db8be0cbf7] [d0a916db8be0cbf7] [] - getDepartments
[boundedElastic-2] [d0a916db8be0cbf7] [fb2367685db70201] [] - found department Department(id=1, nombre=dep, fechaAlta=Fri Apr 24 14:16:20 CEST 2020, staff=1, tag=, empresa=true)
[reactor-http-nio-3] [d0a916db8be0cbf7] [d0a916db8be0cbf7] [] - found department Department(id=1, nombre=dep, fechaAlta=Fri Apr 24 14:16:20 CEST 2020, staff=1, tag=, empresa=true)
请求在 reactor-http-nio 线程中启动,然后切换到 boundedElastic-2。如您所见,显示了 traceId 和 spanId 值,但没有显示我放入过滤器中的值。 Sleuth 如何设法将其上下文 (traceId spanId) 传播到其他线程中?
他们使用 Reactor Context 和 Reactor Hooks。 来自 spring-cloud-sleuth 的相关代码:link 更新:请注意对此有很多更改,请务必检查上面的最新实现link 指向旧提交。
另外几个 link 可能有用:
根据 Martin 在他的回答中提供的信息进行了一些研究,我得出如下结论:
更新过滤器:
@Slf4j
@RequiredArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE + 6)
public class ReactiveRequestCorrelationFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(exchange)
.doOnSubscribe(s -> {
String principal = exchange.getRequest().getHeaders().getFirst("principal-header");
ExtraFieldPropagation.set("principal", principal);
});
}
}
application.yml
spring:
sleuth:
log:
slf4j:
whitelisted-mdc-keys: principal
baggage-keys: principal
有了这个,我的自定义字段开始出现在日志中,Sleuth 负责设置它并从 MDC 中清除它:
[boundedElastic-2] [fadba73bf6447d02] [fadba73bf6447d02] [myprincipal] getDepartaments
您无需任何自定义即可使其正常工作。传播自定义 HTTP headers 所需要做的就是将它们告诉 Sleuth:
#The one is on by default
spring.sleuth.log.slf4j.enabled=true
spring.sleuth.propagation-keys=principal-header
spring.sleuth.log.slf4j.whitelisted-mdc-keys=principal-header
(我使用的是属性版本,但同样适用于 yaml)
然后在你的 logback 配置中使用
[%X{principal-header}]
就是这样!
N.B。我个人更愿意从 header 名称中删除 -header
位。
在请求中添加自定义header以在日志中打印并传播,请按照2.2.6.RELEASE
中的步骤进行例如我的 httpheader 包含一个带有键 'correlationID' 的 header,我希望它打印在日志中并且还需要标记并将它传播给其他人,我不不需要更新 java 代码中的任何内容。像这样,您可以通过更新 applicaiton.yml 文件
直接访问任何 headerapplication.yml
spring:
application:
name: my-app-api
sleuth:
propagation:
tag:
whitelisted-keys:
- correlationID
enabled: true
baggage:
correlation-enabled: true
correlation-fields:
- correlationID
remote-fields:
- correlationID
tag-fields:
- correlationID
trace-id128: true
log:
slf4j:
enabled: true
logging:
pattern:
level: "%5p [${spring.zipkin.service.name:${spring.application.name:-}},%X{X-Span-Export:-},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{correlationID:-}]"
level:
org:
springframework: info
确保日志模式更新为新值以在日志上打印。 (请检查 logging.pattern.level 字段)
需要header
correlationID : sample-correlation-Id-123
并在日志中
2021-01-15 12:02:52.938 INFO [my-app-api,true,6001846c68912f933a714816b62e04a0,3a714816b62e04a0,sample-correlation-Id-123]