日志排查优化-TLog
1.概述
在日常的开发过程中,日志也非常的重要。而当生产环境出现问题时,排查日志是一种非常有效的方式,那么如何快速精准的分析日志就显得尤为重要。
TLog是一款简单、易用、几乎零侵入、适合中小型公司使用的日志追踪框架,它提供了一种最简单的方式来解决日志追踪问题,会自动对日志进行打标签,自动生成traceId贯穿服务的一整条链路,在排查日志的时候,可以根据traceId来快速定位请求处理的链路。但其不收集日志,只是对原来打印的日志增强,将请求链路信息traceId绑定到你打印的日志上。换句话说,就是每次请求时,当前请求就是一条链路,会生成一个链路ID,那么这个ID在同一次的请求中是一样的,那么对于请求的链路追踪就显得格外方便。
2.实战演练
2.1环境准备
这里以SpringBoot项目为例进行说明,日志框架采用logback,仅演示控制台的打印(因为日志不是本文的重点)。
2.2引入TLog
1)导入依赖(这里SpringBoot使用的版本是2.7.2)
<dependency> <groupId>com.yomahub</groupId> <artifactId>tlog-all-spring-boot-starter</artifactId> <version>1.5.0</version> </dependency>
2)修改日志xml配置文件
以下是针对logback的头部日志的encode标签进行替换
<?xml version="1.0" encoding="UTF-8"?> <configuration scan="true" scanPeriod="10 seconds"> <!--1. 输出到控制台--> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <!--替换为AspectLogbackEncoder--> <encoder class="com.yomahub.tlog.core.enhance.logback.AspectLogbackEncoder"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> </appender> <root level="info"> <appender-ref ref="CONSOLE" /> </root> </configuration>
对于同步日志,主要是将encoder或layout的实现类改为tlog的实现类(红色标记的部分);对于异步日志,主要是将appender的实现类改为tlog的实现类。
对于log4j,参考此方式即可,同步日志将layout
的实现类换掉即可,异步日志appender
的实现类替换掉即可。(log4j2无需配置),具体配置见下表:
logback | log4j | log4j2 | ||
同步日志 | encoder |
|
- | 无需修改配置 |
layout |
|
|
无需修改配置 | |
异步日志 | appender |
|
|
无需修改配置 |
3)编写接口
controller:
package com.zxh.test.controller; import com.zxh.test.service.TestServiceImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/test") @Slf4j public class TestController { @Autowired private TestServiceImpl testService; @GetMapping("/test") public void test(String name) { log.info("测试{}", name); testService.test(name); } }
service:
package com.zxh.test.service; import com.zxh.test.util.TestUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @Service @Slf4j public class TestServiceImpl { public void test(String name) { log.info("impl测试{}", name); log.info("impl2测试{}", name); TestUtil.ttt(name); } }
工具类:
package com.zxh.test.util; import lombok.extern.slf4j.Slf4j; @Slf4j public class TestUtil { public static void ttt(String name) { name = name + "-000"; log.info("ttt测试:{}", name); log.info("ittt测试:{}", name); } }
4)测试
在浏览器访问http://localhost:8080/api/test/test?name=899,控制台打印日志如下:
将参数name改为"张三",控制台打印日志如下:
上述的示例更好的说明了TLog的用途。从controller到service,再到工具类,当发起一次请求时,他们的traceId是一致的,就可以把日志串起来,更好的分析整个链路。
3.基本原理
TLogContext是TLog是一个核心的组件,查看源码得知,其使用TransmittableThreadLocal来传递traceId、preApp等信息。当有一个请求过来的时候,会从解析出traceId、preApp等信息设置到TransmittableThreadLocal中,之后就可以在整个调用链路中从TLogContext中获取到traceId等信息。
那么对于微服务或RPC远程调用,其也有处理方式
获取调用方传递的traceId、preApp等信息,设置到TLogContext和MDC中,同时根据日志标签的格式生成日志标签。当然,当获取不到traceId时会重新生成。
它也适配了很多第三方的框架,比如多线程、MQ、跨服务调用等。
TLog默认只打出spanId和traceId,以<$spanId><$traceId>
这种模板打出,当然也可以进行自定义模板,只需要在springboot的application.properties里如下定义:
tlog.pattern=[$preApp][$preIp][$spanId][$traceId]
具体的含义如下:
$preApp
:上游微服务节点名称
$preHost
: 上游微服务的Host Name
$preIp
:上游微服务的IP地址
$spanId
:链路spanId
$traceId
:全局唯一跟踪ID
其中SpanId 代表本次调用在整个调用链路树中的位置,假设一个 Web 系统 A 接收了一次用户请求,那么在这个系统的日志中,记录下的 SpanId 是 0,代表是整个调用的根节点,如果 A 系统处理这次请求,需要通过 RPC 依次调用 B,C,D 三个系统,那么在 A 系统的客户端日志中,SpanId 分别是 0.1,0.2 和 0.3,在 B,C,D 三个系统的服务端日志中,SpanId 也分别是 0.1,0.2 和 0.3;如果 C 系统在处理请求的时候又调用了 E,F 两个系统,那么 C 系统中对应的客户端日志是 0.2.1 和 0.2.2,E,F 两个系统对应的服务端日志也是 0.2.1 和 0.2.2。根据上面的描述,我们可以知道,如果把一次调用中所有的 SpanId 收集起来,可以组成一棵完整的链路树。(来源于官网)
假设一次分布式调用中产生的 TraceId 是 0a1234
,那么根据上文 SpanId 的产生过程,有下图