Spring Cloud Sleuth Servlet和WebFlux获取当前span并添加tag
Servlet自定义Span Tag
对于普通基于spring mvc的web应用,只需要在任何位置依赖brave.Tracer
,就可以拿到当前span,并可给span添加tag:
public class MyFilter extends OncePerRequestFilter {
@Autowired
private Tracer tracer;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
Span span = tracer.currentSpan();
chain.doFilter(request, response);
span.tag("myTag", "myTagValue");
}
}
上面的例子是在Filter中获取当前链路的span,并添加一个Tag,其原理是利用ThreadLocal,把当前请求线程中的Span保存起来,只要是在同一个线程中的操作,都可以获取到当前Span。
WebFlux自定义Span Tag
如果我们直接把servlet获取Span的方法移植过来,会发现始终都是Null,因为Servlet是同步阻塞IO,一个请求会在一个线程中完成,因此可以使用ThreadLocal来保存Span,但是WebFlux基于异步IO,每个线程并不是只为一个请求工作,因此就不能使用ThreadLocal来保存Span。
WebFlux中有一个Context
类,功能可以媲美ThreadLocal
,这个Context保存同一个请求的上下文,但是它并不能像ThreadLocal那样直接获取,只能通过WebFlux订阅:
public class MyFilter implements WebFilter {
@Autowired
private Environment env;
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(exchange).subscriberContext(context -> {
try {
Span span = context.get(Span.class);
if (span != null) {
span.tag("name", env.getProperty("spring.application.name"));
}
} catch (Exception e) {
log.error("span error", e);
}
return context;
});
}
Span放入Context的代码可以在TraceWebFilter
中找到:
...
WebFilterTraceSubscriber(CoreSubscriber<? super Void> actual, Context context,
Span span, MonoWebFilterTrace parent) {
this.actual = actual;
this.span = span;
this.context = context.put(Span.class, span);
this.exchange = parent.exchange;
this.handler = parent.handler;
}
...
不过随着spring-cloud-sleuth的更新,这里的实现逻辑发生了变化,新的版本中不再向context中存放Span:
如果必须从context中获取当前Span,可以通过策略来屏蔽版本差异。
但是除了在contex中存放了span以外,TraceWebFilter还在Exchange中保存了:
因此还可以用下面的方式获取:
public class MyFilter implements WebFilter {
@Autowired
private Environment env;
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(exchange).subscriberContext(context -> {
try {
Span span = (Span) exchange.getAttributes().get(TraceWebFilter.class.getName() + ".TRACE");
if (span != null) {
span.tag("name", env.getProperty("spring.application.name"));
}
} catch (Exception e) {
log.error("span error", e);
}
return context;
});
}