功能说明
为了节约用户的开发成本和提升使用效率,TSF 提供了 Spring Cloud 全链路跟踪的组件。用户只需要在代码中配置组件依赖,即可直接使用 TSF 的全链路跟踪功能,无需关心日志采集、分析、存储等过程。 用户仅需要安装了对应的依赖包及添加依赖项即可,无需其他配置。
微服务对下游组件访问的链路支持
微服务对消息队列组件(Kafka、CMQ、RocketMQ)、网关组件(微服务网关、API 网关)、数据库(Redis、MySQL、postgerSQL、MongoDB)和 httpclient (RestTemplate、Feign)的访问操作会产生跟踪日志, TSF 会对该日志进行采集、分析、统计,这些组件的调用会展现在 TSF 平台的链路追踪中。具体使用说明如下:
说明
前提条件
依赖构建及注解使用
1. 向工程中添加
spring-cloud-tsf-starter
依赖,在 pom.xml 文件中添加以下代码:<dependency><groupId>com.tencent.tsf</groupId><artifactId>spring-cloud-tsf-starter</artifactId><version><!-- 调整为 SDK 长期维护(LTS)版本号 --></version></dependency>
spring-cloud-tsf-starter
中包含了服务注册发现、服务路由、服务鉴权、服务限流、服务熔断、服务容错、服务监控、分布式配置、调用链功能。
2. 向 Application 类中添加注解 @EnableTsf
:// 下面省略了无关的代码import org.springframework.tsf.annotation.EnableTsf;@SpringBootApplication@EnableTsfpublic class ProviderApplication {public static void main(String[] args) {SpringApplication.run(ProviderApplication.class, args);}}
注意
标签与自定义元数据
调用链支持用户在代码中设置标签(Tag) 和自定义元数据(CustomMetada),分别用于调用链的筛选和附带业务信息。
接口定义:
public class TsfContext {/*** 设置多个 Tag。如果有某个 Tag 之前已经被设置过,那么它的值会被覆盖。*/public static void putTags(Map<String, String> tagMap, TagControlFlag... flags) {}/*** 设置 Tag。如果该 key 之前已经被设置过,那么它的值会被覆盖。*/public static void putTag(String key, String value, TagControlFlag... flags) {}/*** 设置 CustomMetadata。*/public static void putCustomMetadata(Object customMetadata)}
注入和获取 trace 信息
TSF SDK 1.23 及之后版本,支持注入和获取 traceId、spanId、tag 信息。
说明
traceId、spanId 格式不建议自定义,可能有未知错误。
TSF 不保证注入后调用链的正确性。
Edgware 版本 SDK 使用方式
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());@Autowiredprivate Tracer tracer;public void addSpan() throw Exception{//获取 traceId、spanId、tagSpan oldSpan = tracer.getCurrentSpan();String traceId = oldSpan.getTraceId();String spanId = oldSpan.getSpanId();Map<String, String> tags = oldSpan.tags();LOG.info("traceId: {}, spanId: {}", traceId, spanId);for (String key : tags.keySet()) {LOG.info("key: {}, value: {}", key, tags.get(key));}//设置 tagtracer.addTag("http.method","get");Map<String, String> tags1 = tracer.getCurrentSpan().tags();for (String key : tags1.keySet()) {LOG.info("key: {}, value: {}", key, tags1.get(key));}//设置 traceId、spanId(必须先新建 span)Span.SpanBuilder spanBuilder = Span.builder();Span span = spanBuilder.name("mockName").traceId(UUID.randomUUID().toString()).spanId(UUID.randomUUID().toString()).tag("key", "value").build();tracer.continueSpan(span);Span newSpan = tracer.getCurrentSpan();String newTraceId = newSpan.getTraceId();String newSpanId = newSpan.getSpanId();LOG.info("traceId: {}, spanId: {}", newTraceId, newSpanId);}
Finchley、Greenwich 版本 SDK 使用方式
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());@Autowiredprivate Tracing tracing;public void addSpan() throw Exception{TraceContext traceContext = tracing.tracer().currentSpan().context();TraceContext context = traceContext.toBuilder().traceId(2035873338086630653L).spanId(-2035873338086630653L).build();CurrentTraceContext currentTraceContext = tracing.currentTraceContext();currentTraceContext.newScope(context);Span spanCur = tracing.tracer().currentSpan();long traceId1 = spanCur.context().traceId();long spanId1 = spanCur.context().spanId();LOG.info("traceId1:{},spanId1:{}",traceId1, spanId1);//添加tagspanCur.tag("qx-test1","value");spanCur.tag("qx-test2","value");spanCur.tag("qx-test1","value");//获取tagField state1 = spanCur.getClass().getDeclaredField("state");state1.setAccessible(true);MutableSpan mutableSpan1 = (MutableSpan)state1.get(spanCur);Field tags1 = mutableSpan1.getClass().getDeclaredField("tags");tags1.setAccessible(true);ArrayList<String> list1 = (ArrayList<String>) tags1.get(mutableSpan1);String key1 =null, value1 =null;for (int i = 0, length = list1.size(); i < length; i += 2) {key1 = list1.get(i);value1 = list1.get(i + 1);LOG.info("tags key1: {}, value1: {}", key1, value1);}//获取 traceid、spanidTraceContext traceContext1 = spanCur.context();long traceId2 = traceContext1.traceId();long spanId2 = traceContext1.spanId();LOG.info("traceId1:{},spanId1:{}",traceId2, spanId2);}
异步线程注入调用链信息(以 Finchley、Greenwich 版 SDK 为例)
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());@Autowiredprivate Tracing tracing;public void setTraceContext() {LOG.info("setTraceContext start");// 获取调用链上下文信息Span spanCur = tracing.tracer().currentSpan();TraceContext traceContextCur = spanCur.context();long traceIdCur = traceContextCur.traceId(); // traceId 是 64 位二进制的 long 类型的非零随机数long spanIdCur = traceContextCur.spanId();String traceIdStringCur = traceContextCur.traceIdString(); // traceIdString 是 16 位十六进制的字符串,用于向下游传递String spanIdStringCur = HexCodec.toLowerHex(spanIdCur);LOG.info("traceId: {}, spanId: {}, traceIdString: {}, spanIdString: {}", traceIdCur, spanIdCur, traceIdStringCur, spanIdStringCur);ExecutorService executorService = Executors.newCachedThreadPool();executorService.execute(() -> {// 异步线程,调用链上下文信息为空LOG.info("new thread, trace context is empty");// 注入调用链上下文信息TraceContext traceContext = TraceContext.newBuilder().traceId(traceIdCur).spanId(spanIdCur).sampled(true).build();CurrentTraceContext currentTraceContext = tracing.currentTraceContext();currentTraceContext.newScope(traceContext);// 注入后获取调用链上下文信息Span spanThread = tracing.tracer().currentSpan();TraceContext traceContextThread = spanThread.context();long traceIdThread = traceContextThread.traceId();long spanIdThread = traceContextThread.spanId();String traceIdStringThread = traceContextThread.traceIdString();String spanIdStringThread = HexCodec.toLowerHex(spanIdThread);LOG.info("in thread, traceId: {}, spanId: {}, traceIdString: {}, spanIdString: {}", traceIdThread, spanIdThread, traceIdStringThread, spanIdStringThread);});executorService.shutdown();}
调整采样率
在 application.yml 或者 bootstrap.yml 添加如下配置:
tsf:sleuth:samplerRate: 0.1
说明
samplerRate 默认为1,采样率为100%;samplerRate 设置为0.1时,采样率为10%,以此类推。