SpringCloud官网中关于Sleuth链路添加额外传播字段的描述如下:
有时需要传播额外的字段,例如请求 ID 或备用跟踪上下文。例如,如果你在 Cloud Foundry 环境中,可能希望传递请求 ID,如下例所示:
// when you initialize the builder, define the extra field you want to propagate
Tracing.newBuilder().propagationFactory(
ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "x-vcap-request-id")
);
// later, you can tag that request ID or use it in log correlation
requestId = ExtraFieldPropagation.get("x-vcap-request-id");
即在初始化时,添加需要额外传播的字段。在之后需要用到的地方可以通过ExtraFieldPropagation.get("x-vcap-request-id")
的方式来获取。
在具体项目中的用法:
我们公司的需求是:链路追踪不止可以记录后台微服务的的调用链,也要把前端用户的操作单元给记录下来。
实现方式:由前端传两个标识参数来记录用户操作的链路,在网关时,获取到传递过来的参数,然后通过Brave的额外传播字段进行链路捆绑,并在客户端(具体的微服务应用)进行配置,将 baggage 值设置为 Slf4j 的 MDC,然后即可在日志中输出该参数信息。
一.定义额外传播的字段:
在网关中定义传播的额外字段,经测试,有两种方式可以添加额外的字段
方式一:
@Configuration
@Order(TraceWebServletAutoConfiguration.TRACING_FILTER_ORDER - 2)
public class SleuthPropagateConfig {
/** 前端操作ID */
public static final String OPERATION_ID = "X-Operation-ID";
/** 前端操作名称 如:行政区划/保存 */
public static final String OPERATION_NAME = "X-Operation-Name";
@Bean
public SleuthProperties sleuthProperties() {
SleuthProperties sleuthProperties = new SleuthProperties();
//会加baggage-前缀
// List<String> dkBaggageKeys = new ArrayList<>();
// dkBaggageKeys.add(OPERATION_ID);
// List<String> baggageKeys = sleuthProperties.getBaggageKeys();
// if (CollectionUtils.isEmpty(baggageKeys)) {
// sleuthProperties.setBaggageKeys(dkBaggageKeys);
// return sleuthProperties;
// }
// if (!baggageKeys.contains(OPERATION_ID)) {
// sleuthProperties.getBaggageKeys().add(OPERATION_ID);
// }
// if (!baggageKeys.contains(OPERATION_NAME)) {
// sleuthProperties.getBaggageKeys().add(OPERATION_NAME);
// }
//不会加前缀
List<String> frontTraceKeys = new ArrayList<>();
frontTraceKeys.add(OPERATION_ID);
frontTraceKeys.add(OPERATION_NAME);
List<String> propagationKeys = sleuthProperties.getPropagationKeys();
if (CollectionUtils.isEmpty(propagationKeys)) {
sleuthProperties.setPropagationKeys(frontTraceKeys);
return sleuthProperties;
}
if (!propagationKeys.contains(OPERATION_ID)) {
sleuthProperties.getPropagationKeys().add(OPERATION_ID);
}
if (!propagationKeys.contains(OPERATION_NAME)) {
sleuthProperties.getPropagationKeys().add(OPERATION_NAME);
}
return sleuthProperties;
}
}
但是这种方式不是很优雅,本人用的是第二种方式
方式二:
@Configuration
@Order(TraceWebServletAutoConfiguration.TRACING_FILTER_ORDER - 2)
public class SleuthPropagateConfig {
/** 前端操作ID */
public static final String OPERATION_ID = "X-Operation-ID";
/** 前端操作名称 如:行政区划/保存 */
public static final String OPERATION_NAME = "X-Operation-Name";
/**
* 定义链路追踪额外传播字段
* @return
*/
@Bean
public Tracing tracing() {
return Tracing.newBuilder().propagationFactory(
ExtraFieldPropagation.newFactoryBuilder(B3Propagation.FACTORY)
.addField(OPERATION_NAME)
.addField(OPERATION_ID)
//.addPrefixedFields("x-baggage-", Arrays.asList(OPERATION_NAME, OPERATION_ID))
.build()
).build();
}
}
二.在网关过滤器中设置传播字段的信息
@Component
public class FrontTraceFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String operationId = exchange.getRequest().getQueryParams().getFirst(SleuthPropagateConfig.OPERATION_ID);
String operationName = exchange.getRequest().getQueryParams().getFirst(SleuthPropagateConfig.OPERATION_NAME);
if (!StringUtils.isEmpty(operationId)
&& StringUtils.isEmpty(ExtraFieldPropagation.get(SleuthPropagateConfig.OPERATION_ID))) {
ExtraFieldPropagation.set(SleuthPropagateConfig.OPERATION_ID, operationId);
}
if (!StringUtils.isEmpty(operationName)
&& StringUtils.isEmpty(ExtraFieldPropagation.get(SleuthPropagateConfig.OPERATION_NAME))) {
ExtraFieldPropagation.set(SleuthPropagateConfig.OPERATION_NAME, operationName);
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
三.在客户端(微服务应用)添加配置
在application.yml中添加额外传播字段的相关配置,sleuth2.0后必须配置,否则在客户端获取不到字段信息
spring:
application:
name: demo-feign2
sleuth:
sampler:
#调用链信息采样率
probability: 1.0
# 注意, Sleuth2.0.0之后, baggage的 key 必须在这里配置才能生效
# baggage-keys:
# - X-Operation-ID
# - X-Operation-Name
propagation-keys:
- X-Operation-ID
- X-Operation-Name
log:
slf4j:
#自动额外传播字段设置为 Slf4j 的 MDC
whitelisted-mdc-keys:
- X-Operation-ID
- X-Operation-Name
四.在客户端中使用额外的传播字段
ExtraFieldPropagation.get("key");
五.logback日志配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
<springProperty scope="context" name="LOG_FILE" source="logging.path"/>
<springProperty scope="context" name="ServerPort" source="server.port"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date [%thread] %-5level %logger{80} - %msg%n</pattern>
</encoder>
</appender>
<appender name="LOGSTASH" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE}.json</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE}/${APP_NAME}.json.%d{yyyy-MM-dd}.gz</fileNamePattern>
<maxHistory>7</maxHistory>
</rollingPolicy>
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp>
<timeZone>UTC</timeZone>
</timestamp>
<pattern>
<pattern>
{
"level": "%level",
"service": "${APP_NAME:-}",
"traceId": "%X{X-B3-TraceId:-}",
"spanId": "%X{X-B3-SpanId:-}",
"parentSpanId": "%X{X-B3-ParentSpanId:-}",
"exportable": "%X{X-Span-Export:-}",
"pid": "${PID:-}",
"thread": "%thread",
"class": "%logger{40}",
"method": "%M",
"line": "%L",
"message": "%message",
"operationId": "%X{X-Operation-ID}"
"operationName": "%X{X-Operation-Name}"
}
</pattern>
</pattern>
</providers>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="LOGSTASH" />
<appender-ref ref="STDOUT" />
</root>
</configuration>
六.logback日志输出结果
{"@timestamp":"2019-11-27T04:00:33.233+00:00","level":"INFO","service":"demo-feign2","traceId":"04f1bfb59beb7053","spanId":"c7ba04760057c6f2","parentSpanId":"04f1bfb59beb7053","exportable":"true","pid":"43252","thread":"http-nio-8302-exec-2","class":"com.caihua.demo.feign.web.HiController","method":"sayHi","line":"19","message":"this is feign2 controller, begin to call the feign service","operationId":"123","operationName":"save"}
{"@timestamp":"2019-11-27T04:00:33.243+00:00","level":"INFO","service":"demo-producer2","traceId":"04f1bfb59beb7053","spanId":"9ece8de790d6ad9e","parentSpanId":"c7ba04760057c6f2","exportable":"true","pid":"7968","thread":"http-nio-8301-exec-4","class":"com.caihua.demo.DemoProducer2Application","method":"home","line":"29","message":"this is producer controller, begin to call producer2 service ","operationId":"123","operationName":"save"}
{"@timestamp":"2019-11-27T04:00:33.244+00:00","level":"INFO","service":"demo-producer2","traceId":"04f1bfb59beb7053","spanId":"9ece8de790d6ad9e","parentSpanId":"c7ba04760057c6f2","exportable":"true","pid":"7968","thread":"http-nio-8301-exec-4","class":"com.caihua.demo.producer.HiService","method":"sayHi","line":"21","message":"this is producer2 HiService, i will call private method","operationId":"123","operationName":"save"}
{"@timestamp":"2019-11-27T04:00:33.244+00:00","level":"INFO","service":"demo-producer2","traceId":"04f1bfb59beb7053","spanId":"9ece8de790d6ad9e","parentSpanId":"c7ba04760057c6f2","exportable":"true","pid":"7968","thread":"http-nio-8301-exec-4","class":"com.caihua.demo.producer.HiService","method":"sayHello","line":"28","message":"hello: cai, this is private method","operationId":"123","operationName":"save"}
网友评论