一、环境搭建
1.1包配置
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
1.2日志配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration scanPeriod="60 seconds" debug="false">
<!-- https://docs.spring.io/spring-boot/docs/current/reference/html/howto-logging.html -->
<property name="CONSOLE_LOG_PATTERN"
value="[%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) %X{X-B3-TraceId} %X{traceId} %yellow(%X{X-B3-TraceId}),%green(%X{X-B3-SpanId}),%blue(%X{X-B3-ParentSpanId}) %yellow(%thread) %green(%logger) %msg%n"/>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<!--<include resource="org/springframework/boot/logging/logback/base.xml"/>-->
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
二、实现分析
2.1 sleuth日志插件
org.springframework.cloud.sleuth.log.Slf4jCurrentTraceContext#newScope
- 获取父span设置的信息
2)将当前span的信息设置到MDC中,如果当前span为空,需要从MDC中移除。
3)当span close时,将MDC设置为父级span信息。
@Override public Scope newScope(@Nullable TraceContext currentSpan) {
final String previousTraceId = MDC.get("traceId");
final String previousParentId = MDC.get("parentId");
final String previousSpanId = MDC.get("spanId");
final String spanExportable = MDC.get("spanExportable");
final String legacyPreviousTraceId = MDC.get(LEGACY_TRACE_ID_NAME);
final String legacyPreviousParentId = MDC.get(LEGACY_PARENT_ID_NAME);
final String legacyPreviousSpanId = MDC.get(LEGACY_SPAN_ID_NAME);
final String legacySpanExportable = MDC.get(LEGACY_EXPORTABLE_NAME);
if (currentSpan != null) {
String traceIdString = currentSpan.traceIdString();
MDC.put("traceId", traceIdString);
MDC.put(LEGACY_TRACE_ID_NAME, traceIdString);
String parentId = currentSpan.parentId() != null ?
HexCodec.toLowerHex(currentSpan.parentId()) :
null;
replace("parentId", parentId);
replace(LEGACY_PARENT_ID_NAME, parentId);
String spanId = HexCodec.toLowerHex(currentSpan.spanId());
MDC.put("spanId", spanId);
MDC.put(LEGACY_SPAN_ID_NAME, spanId);
2.2 Logback获取MDC信息
Logback对象层级关系:Logger -> Appender -> Encoder -> PatternLayout,Layout中head定义了pattern的convert。
ch.qos.logback.core.OutputStreamAppender#subAppend
subAppend调用prepareForDeferredProcessing设置MDC。
public void prepareForDeferredProcessing() {
this.getFormattedMessage();
this.getThreadName();
// fixes http://jira.qos.ch/browse/LBCLASSIC-104
this.getMDCPropertyMap();
}
2.3 MDCConvert
%X{X-B3-TraceId}被定义为MDCConverter,它从MDC中获取信息。
@Override
public String convert(ILoggingEvent event) {
Map<String, String> mdcPropertyMap = event.getMDCPropertyMap();
if (mdcPropertyMap == null) {
return defaultValue;
}
if (key == null) {
return outputMDCForAllKeys(mdcPropertyMap);
} else {
String value = mdcPropertyMap.get(key);
if (value != null) {
return value;
} else {
return defaultValue;
}
}
}
image.png
网友评论