目录
[2.1 自定义链路追踪应用场景](#2.1 自定义链路追踪应用场景)
[2.2 链路追踪几个关键概念](#2.2 链路追踪几个关键概念)
[三、skywalking 自定义链路追踪实现](#三、skywalking 自定义链路追踪实现)
[3.1 环境准备](#3.1 环境准备)
[3.2 集成过程](#3.2 集成过程)
[3.2.1 导入核心依赖](#3.2.1 导入核心依赖)
[3.2.2 几个常用注解](#3.2.2 几个常用注解)
[3.2.3 方法集成](#3.2.3 方法集成)
[3.2.4 上报追踪信息](#3.2.4 上报追踪信息)
[四、skywalking 自定义日志采集](#四、skywalking 自定义日志采集)
[4.1 概述](#4.1 概述)
[4.2 集成过程](#4.2 集成过程)
[4.2.1 引入核心依赖](#4.2.1 引入核心依赖)
[4.2.2 配置logback文件](#4.2.2 配置logback文件)
[4.2.2 接口测试](#4.2.2 接口测试)
一、前言
在前面的分享中,详细了解了在微服务中集成skywalking进行服务链路追踪的过程,不管是使用dubbo还是springcloud做微服务治理,在集成skywalking时,往往关注的更多是不同服务之间的调用情况,但是在实际应用中,如果服务内部的链路调用比较复杂,跨度很长的情况下,是否也可以使用skywalking进行追踪呢?接下来将详细介绍下。
二、自定义链路追踪简介
2.1 自定义链路追踪应用场景
在下面的场景下,可能需要在单应用内进行追踪:
- 接口业务逻辑复杂,内含一些关键的远程接口调用;
- 业务调用链路较长,内部方法调用栈比较深;
- 需要监控某些关键执行业务逻辑的执行耗时,它们可能是性能瓶颈点;
- ...
比如下面的一个接口,内部的逻辑调用链路比较深的情况下就需要自定义链路追踪

2.2 链路追踪几个关键概念
在具体学习自定义链路追踪之前,有下面几个概念需要了解
Trace
Trace就是链路,指一个请求经过所有服务的路径,服务间经过的局部链路构成了一条完整的链路,其中每一条局部链路都用一个全局唯一的traceid来标识。
Span
Span用于表示上下层父子关系,同一层级parent id相同,span id不同,span id从小到大表示请求的顺序。通过事先在日志中埋点,找出相同traceId的日志,再加上parent id和span id就可以将一条完整的请求调用链串联起来。
采样
由于每一个请求都会生成一个链路,为了减少性能消耗,避免存储资源的浪费,采集器并不会上报所有的span数据,而是使用采样的方式。举个例子,每秒有1000个请求访问系统,如果设置采样率为1/1000,那么只会上报一个请求到存储端。
存储
链路中的span数据经过收集和上报后会集中存储在一个地方,常用的存储有Mysql,ElasticSearch, HBase, In-memory DB等。
三、skywalking 自定义链路追踪实现
3.1 环境准备
- 参照之前的文章提前搭建完skywalking 服务,并能访问web-ui界面;
- 搭建一个springboot工程;
- 启动nacos服务;

3.2 集成过程
下面演示如何在springboot工程中集成方法级的链路追踪信息并上报skywalking 。
3.2.1 导入核心依赖
在方法中记录追踪信息主要用到apm-toolkit-trace这个依赖,其提供了丰富的注解可供方法使用。
java
<dependencies>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>8.14.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
3.2.2 几个常用注解
在使用apm-toolkit-trace时,通常使用其提供的注解用于标准方法和参数,下面来看看常用的几个注解使用。
使用方式
java
@Trace
@Tag(key = "tag1", value = "arg[0]")
@Tag(key = "tag2", value = "arg[1]")
@Tag(key = "username", value = "returnedObj.username")
@Tag(key = "age", value = "returnedObj.age")
public User getUser(String param1, String param2) {
// ActiveSpan.setOperationName("Customize your own operation name, if this is an entry span, this would be an endpoint name");
// ...
}
参数说明:
@Trace
用在方法上,表名这是一个方法级的trace,@Trace也可以定义名称,比如@Trace(operationName = "getUserInfo"),@Trace注解其只能在方法上进行注解,使用operationName属性指定Span的名字,若不指定,会使用方法名;
@Tags/ @Tag
使用@Tags/ @Tag注解添加Span的属性
1)key 属性名
2)value 属性值会是一个表达式,具体可以参考官方文档,具体可参考,参考文档
3)arg[0]代表入参的第一个对象,returnedObj即当前方法返回的对象;
3.2.3 方法集成
在工程中创建一个接口
java
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/get")
public Object getUserInfo(String userId){
Map userInfo = userService.getUserInfo(userId);
return userInfo;
}
}
业务实现
java
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Override
@Trace(operationName = "getUserInfo")
@Tags({
@Tag(key = "userId", value = "arg[0]"),
@Tag(key = "user", value = "returnedObj")
}
)
public Map getUserInfo(String userId) {
//获取自定义的上下文中的追踪信息
String traceId = TraceContext.traceId();
log.info("准备获取用户信息 ,traceId : [{}]", traceId);
Map resMap = new HashMap();
UserInfo userInfo = new UserInfo(userId, "jerry");
resMap.put("user", userInfo);
//获取用户所属的组织机构
log.info("准备获取组织机构信息 ,traceId : [{}]", traceId);
Depart depart = getDepart(userId);
resMap.put("depart", depart);
return resMap;
}
@Trace(operationName = "getDepart")
@Tags(
@Tag(key = "depart", value = "returnedObj")
)
private Depart getDepart(String userId) {
if (userId == null) {
return null;
}
return new Depart("001", "运维部");
}
}
在该方法中,我们要实现的逻辑是,根据用户ID获取用户,同时在获取用户信息时还需要拿到组织机构信息,为了使用自定义追踪,在方法上添加了相关的注解,在不接入skywalking 的agent时启动测试接口

3.2.4 上报追踪信息
在启动参数中添加如下信息
bash
-javaagent:E:\code-self\skywalking-agent\skywalking-agent.jar -DSW_AGENT_NAME=sky-service -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=skywalking IP:11800
再次启动服务,并调用上面的接口,从控制台可以看到此时当前接口请求就产生了一条追踪信息,可以看到输出的traceId;

同时在web-ui界面上,可以看到当前请求的处理方法中各个链路的详情,即从接口出发,到接口中处理的各个环节,包括调用的其他方法都能展示出来

点击到具体的方法中,比如点击getUserInfo这个方法中,能够看到追踪的该方法具体的信息,比如参数,返回值

四、skywalking 自定义日志采集
4.1 概述
在实际项目中,为了方便线上排查问题,尤其是微服务之间调用链路比较复杂的系统中,通过可视化日志的手段仍然是最直接也很方便的排查定位问题的手段,比如大家熟悉的ELK就是一种比较成熟的可视化日志展现方式,在skywalking UI界面上,在服务菜单栏中有Log一项,该项就是用于服务中集成日志,然后上报skywalking 进行链路的分析使用。

4.2 集成过程
微服务要通过skywalking 集成和上报日志信息,需要依赖logback日志,因此在集成过程中,需要按照规范定义logback的文件配置,整个过程分为下面几步:
-
引入logback依赖;
-
配置logback文件,按照规范输出日志信息;
-
导出日志并上报到skywalking ;
接下来以上一篇的springcloud接入skywalking 为例,在此基础上继续接入自定义的日志
4.2.1 引入核心依赖
在各个微服务模块中引入如下依赖
java
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
<version>8.14.0</version>
</dependency>
4.2.2 配置logback文件
参考下面的样例配置logback文件,核心配置说明:
-
在第一个ConsoleAppender中,Pattern中定义的内容表示日志输出格式,其中最关键的就是tid,即通过日志追记录的traceId;
-
第二个AsyncAppender使用的是异步方式追加,减少对主业务的性能影响;
-
GRPCLogClientAppender,以grpc的方式上报日志到skywalking 的oap服务;
java
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod=" 5 seconds">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{tid}] [%thread] %-5level %logger{36} -%msg%n</Pattern>
</layout>
</encoder>
</appender>
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>1024</queueSize>
<neverBlock>true</neverBlock>
<appender-ref ref="STDOUT"/>
</appender>
<appender name="grpc-log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{tid}] [%thread] %-5level %logger{36} -%msg%n</Pattern>
</layout>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="ASYNC"/>
<appender-ref ref="grpc-log"/>
</root>
</configuration>
4.2.2 接口测试
启动几个模块的微服务,nacos中可以看到服务已经注册进去

通过网关调用接口:localhost:9001/os/order/getById?id=001,可以看到接口能够响应数据

观察控制台的输出日志,不难发现,接口从order中发起,然后远程调用user服务中查询用户的逻辑,观察order服务的日志输出,产生了一个TID,即上午所说的traceId;

再检查user服务的控制台日志输出,同样输出了TID,细心的同学会发现,这个TID与上面的TID相同,因为是同一条链路的唯一ID;

此时再去WEB-UI界面,可以看到在Log这一栏中,就产生了相关的日志信息

可以在服务下拉框里面选择某个服务进行过滤,比如选择order服务之后点击某条日志进去检查,就能看到这条日志的详细信息


五、写在文末
通过上面的演示过程,演示了如何在微服务中自定义方法级的链路追踪,以及通过自定义输出格式的日志上报到skywalking的完整过程,在实际项目使用中,还需要结合实际需求进行细节上的调整,希望对看到的同学有用,本篇到此结束,感谢观看。