问题
在继承了SkyWalking日志工具的情况下,Spring Cloud Gateway的日志中TID为N/A,其他服务的日志均能够正常记录TID。
SkyWalking UI可以正常记录包括Spring Cloud Gateway在内的各个服务的TID。
原因
SkyWalking agent plugin只保证将TID传递给OAP,而不保证一定把TID输出到日志。
如果需要支持,则需要使用方增加此特性。
Agent plugin源码分析
Spring Cloud Gateway对应的plugin源码如下:
Agent plugin:spring-webflux-5.x-plugin
类:DispatcherHandlerHandleMethodInterceptor
@Override
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
MethodInterceptResult result) throws Throwable {
EnhancedInstance instance = getInstance(allArguments[0]);
ServerWebExchange exchange = (ServerWebExchange) allArguments[0];
ContextCarrier carrier = new ContextCarrier();
CarrierItem next = carrier.items();
HttpHeaders headers = exchange.getRequest().getHeaders();
while (next.hasNext()) {
next = next.next();
List<String> header = headers.get(next.getHeadKey());
if (header != null && header.size() > 0) {
next.setHeadValue(header.get(0));
}
}
AbstractSpan span = ContextManager.createEntrySpan(exchange.getRequest().getURI().getPath(), carrier);
if (instance != null && instance.getSkyWalkingDynamicField() != null) {
ContextManager.continued((ContextSnapshot) instance.getSkyWalkingDynamicField());
}
span.setComponent(ComponentsDefine.SPRING_WEBFLUX);
SpanLayer.asHttp(span);
Tags.URL.set(span, exchange.getRequest().getURI().toString());
HTTP.METHOD.set(span, exchange.getRequest().getMethodValue());
instance.setSkyWalkingDynamicField(ContextManager.capture());
span.prepareForAsync();
ContextManager.stopSpan(span);
//SKYWALING_SPAN保存到exchange
exchange.getAttributes().put("SKYWALING_SPAN", span);
}
Solution
Solution 1
- 只需要在filter中记录TID,便于排查问题,而不保存到日志框架的MDC种。
- 根据源码分析部分可以得知plugin会把SKYWALING_SPAN保存到exchange
中,所以我们可以在exchange中获取SKYWALING_SPAN。 - 从exchange中获取到Span对象后,需要通过反射的方式一层层获取到traceid,因为无法直接依赖包含这些类的agent-core包,会跟agent本身发生冲突。
String traceId = "N/A";
Object skywalingSpanObject = exchange.getAttributes().get("SKYWALING_SPAN");
if (ObjectUtils.isNotEmpty(skywalingSpanObject)) {
try {
Field owner = FieldUtils.getField(skywalingSpanObject.getClass(), "owner", true);
Object tracingContext = owner.get(skywalingSpanObject);
Field segmentField = FieldUtils.getField(tracingContext.getClass(), "segment", true);
Object segment = segmentField.get(tracingContext);
Field relatedGlobalTraceIdField = FieldUtils.getField(segment.getClass(), "relatedGlobalTraceId", true);
Object relatedGlobalTraceId = relatedGlobalTraceIdField.get(segment);
String traceIdObject = relatedGlobalTraceId.toString();
traceId = Stringutils.substrΩingBetween(traceIdObject, "=", ")");
} catch (Exception e) {
log.warn("get TID failed", e);
}
Reference
SpringCloudGateway3.1.3, With apm-log4j2-2.x:8.10.0, does not display the real traceId, always display 'TID: N/A' · Discussion #9232 · apache/skywalking (github.com)
My log can't get traceId when I use Spring Cloud Gateway in my project ,all traceId in my log is "[traceId:TID:N/A]" · Issue #5268 · apache/skywalking (github.com)
SpringCloudGateway使用Skywalking时日志打印traceId - 简书 (jianshu.com)
#SkyWalking
#SpringCloudGateway
#MDC
#TracdID