轻量级分布式日志标记追踪神器,十分钟即可接入到项目!

点击上方“Java基基”,选择“设为星标”

做积极的人,而不是积极废人!

每天 14:00 更新文章,每天掉亿点点头发...

源码精品专栏

 

来源:blog.csdn.net/weixin_41541562/

article/details/123629617

a1aaeeb83668ba211dfd1f19b0feca44.jpeg


一. TLog简介

TLog提供了一种最简单的方式来解决日志追踪问题,它不收集日志,也不需要另外的存储空间,它只是自动的对你的日志进行打标签,自动生成TraceId贯穿你微服务的一整条链路。并且提供上下游节点信息。适合中小型企业以及想快速解决日志追踪问题的公司项目使用。

支持dubbo,dubbox,spring cloud三大RPC框架。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro

  • 视频教程:https://doc.iocoder.cn/video/

二. TLog安装

在SpringBoot项目引入全量依赖

<dependency>
  <groupId>com.yomahub</groupId>
  <artifactId>tlog-all-spring-boot-starter</artifactId>
  <version>1.3.6</version>
</dependency>

按需引入依赖

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud

  • 视频教程:https://doc.iocoder.cn/video/

三. 适配方式

支持Log4j、Logback、Log4j2三大日志框架

1. Logback框架适配器
1.1. 同步日志

换掉encoder的实现类或者换掉layout的实现类就可以了

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <property name="APP_NAME" value="logtest"/>
    <property name="LOG_HOME" value="./logs" />
    <appender name="STDOUT" 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>
    <appender name="FILE"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>${LOG_HOME}/${APP_NAME}.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <FileNamePattern>${LOG_HOME}/${APP_NAME}.log.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
            <MaxHistory>30</MaxHistory>
            <maxFileSize>1000MB</maxFileSize>
        </rollingPolicy>
        <!--这里替换成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="STDOUT" />
        <appender-ref ref="FILE" />
    </root>
</configuration>
1.2. 异步日志

替换掉appender的实现类就可以了

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <property name="APP_NAME" value="logback-dubbo-provider"/>
    <property name="LOG_HOME" value="./logs" />
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>
    <appender name="FILE"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>${LOG_HOME}/${APP_NAME}.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <FileNamePattern>${LOG_HOME}/${APP_NAME}.log.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
            <MaxHistory>30</MaxHistory>
            <maxFileSize>1000MB</maxFileSize>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 这里替换成AspectLogbackAsyncAppender -->
    <appender name="ASYNC_FILE" class="com.yomahub.tlog.core.enhance.logback.async.AspectLogbackAsyncAppender">
        <discardingThreshold>0</discardingThreshold>
        <queueSize>2048</queueSize>
        <includeCallerData>true</includeCallerData>
        <appender-ref ref="FILE"/>
    </appender>

    <root level="INFO">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="ASYNC_FILE" />
    </root>
</configuration>

四. 日志标签模板自定义

TLog默认只打出spanId和traceId,以<$spanId><$traceId>这种模板打出,当然你能自定义其模板。还能加入其它的标签头

你只需要在springboot的application.properties里如下定义:

tlog.pattern=[$preApp][$preIp][$spanId][$traceId]
  • $preApp:上游微服务节点名称

  • $preHost: 上游微服务的Host Name

  • $preIp:上游微服务的IP地址

  • $spanId:链路spanId

  • $traceId:全局唯一跟踪ID

这样日志的打印就能按照你定义模板进行打印

五. 标签位置自定义

TLog默认的标签都是跟在具体日志信息体的前面的,但是如果想自定义位置怎么办,比如放在[INFO]的前面,有没有办法?

TLog从1.1.5开始,适配了slf4j的mdc,从而能够让你的标签在一行日志的任意位置打印出。但是只支持日志框架配置方式接入。

TLog中log4j和logback的占位符为%X{tl},而在log4j2中的占位符为%TX{tl},请注意不要写错

你可以在任意日志框架的pattern里定义。例如在logback里,你可以这样定义pattern:

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="./logs" />
    <!--控制台日志-->
    <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} %X{tl} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--文件日志-->
    <!--同步日志-->
    <appender name="SyncLogFile"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>${LOG_HOME}/logback-sync-rolling-mdc.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/logback-rolling-mdc.log.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
            <!--日志文件大小-->
            <maxFileSize>1000MB</maxFileSize>
        </rollingPolicy>
        <encoder class="com.yomahub.tlog.core.enhance.logback.AspectLogbackEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %X{tl} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 日志输出级别 -->
    <root level="INFO">
        <appender-ref ref="Console" />
        <appender-ref ref="SyncLogFile" />
    </root>
</configuration>

那么出来的日志范例为:

2020-11-07 17:01:26.647 <0.1><7455616963803840> [Thread-8] INFO  c.y.tlog.example.dubbox.service.impl.AsynDomain - 这是测试日志哦

在log4j和log4j2中同理。

六. SpanId的生成规则

TLog默认的标签打印模板是<$spanId><$traceId>

TLog 中的 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 的产生过程,有下图:

a4c6a6be357101a6578a054a13d25608.png

七. 业务自定义标签

很多公司的系统在打日志的时候,每打一个日志里都会带入一些业务信息,比如记录ID,会员CODE,方便业务日志的定位。现在有了TLog,不仅能做分布式链路标签追加,还能自动帮你做业务标签的添加。这样在定位日志的时候可以更加方便的搜索。

Tlog支持方法级别的自定义业务标签。你可以在方法上定义简单的标注,来实现在某一个方法的日志里,统一加入业务的指标标签,用于更加细致的定位。

使用@TLogAspect

示例一 打印入参
@TLogAspect(value = {"id","name"},pattern = "<-{}->",joint = "_")
public void demo(String id,String name){
  log.info("加了patter和joint的示例");
}

日志打出来的样子如下,其中前面为框架spanId+traceId

2020-02-08 22:09:40.103 [main] INFO  Demo - <0.2><7205781616706048><-NO1234_jenny-> 加了patter和joint的示例
示例二 常量标签
@TLogAspect(str = "XYZ")
public void demo1(String name){
  log.info("这是第一条日志");
  log.info("这是第二条日志");
  log.info("这是第三条日志");
  new Thread(() -> log.info("这是异步日志")).start();
}
2020-02-08 20:22:33.945 [main] INFO  Demo - <0.2><7205781616706048>[XYZ] 这是第一条日志
2020-02-08 20:22:33.945 [main] INFO  Demo - <0.2><7205781616706048>[XYZ] 这是第二条日志
2020-02-08 20:22:33.945 [main] INFO  Demo - <0.2><7205781616706048>[XYZ] 这是第三条日志
2020-02-08 20:22:33.948 [Thread-3] INFO  Demo - <0.2><7205781616706048>[XYZ] 这是异步日志
示例三 点操作符

支持类型:

  • Bean对象

  • Map对象

  • Json格式的字符串

  • Fastjson的JSONObject对象

@TLogAspect({"person.id","person.age","person.company.department.dptId"})
public void demo(Person person){
  log.info("多参数加多层级示例");
}
2020-02-08 22:09:40.110 [main] INFO  Demo - <0.2><7205781616706048>[31-25-80013] 多参数加多层级示例

可以用点操作符操作符合Json格式的字符串

@TLogAspect({"person.id","person.age","person.company.department.dptId"})
public void demo(String person){
  log.info("多参数加多层级示例");
}

甚至可以用下标[num]去访问Json格式的字符串中的数组

@TLogAspect({"person.id","person.age","person.company.title[1]"})
public void demo(String person){
  log.info("多参数加多层级示例");
}
示例四 自定义Convertor

自定义Convert,适用于更复杂的业务场景

@TLogAspect(convert = CustomAspectLogConvert.class)
public void demo(Person person){
  log.info("自定义Convert示例");
}
public class CustomAspectLogConvert implements AspectLogConvert {
    @Override
    public String convert(Object[] args) {
        Person person = (Person)args[0];
        return "PERSON(" + person.getId() + ")";
    }
}
2020-02-20 17:05:12.414 [main] INFO  Demo - <0.2><7205781616706048>[PERSON(31] 自定义Convert示例

八. 线程支持

1. 一般异步线程

异步线程的定义为:执行好之后线程会自动销毁,如new Thread(){…}.start这种内部类线程,你自定定义的类继承Thread或实现Runnerable接口的。

对于一般异步线程,不需要你做任何事,TLog天然支持在异步线程中打印标签。

2. 线程池

但是对于使用了线程池的场景,由于线程池中的线程不会被销毁,会被复用。需要你用TLogInheritableTask替换Runnable,否则标签数据会重复:

ExecutorService pool = Executors.newFixedThreadPool(5);
pool.submit(new TLogInheritableTask() {
    @Override
    public void runTask() {
      log.info("我是异步线程日志");
    }
});
3. MDC模式的异步线程

如果你用了标签位置自定义功能(这个功能是用slf4j的MDC来实现的),那么你有可能发现,在MDC模式中的异步线程好像都没法正常打印标签。这是因为MDC中使用了ThreadLocal,而ThreadLocal中的信息没法传递给子线程。为此TLog提供了线程的包装类,能帮助你解决这个问题。

还是同样的类TLogInheritableTask,如果你声明一个线程,你需要去继承TLogInheritableTask

public class AsynDomain extends TLogInheritableTask {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Override
    public void runTask() {
        log.info("这是异步方法哦");
        log.info("异步方法开始");
        log.info("异步方法结束");
    }
}

执行的时候和普通继承了Thread一样:

new AsynDomain().start();

如果你要在线程池中执行,同样以AsynDomain为例,则为:

ExecutorService pool = Executors.newFixedThreadPool(5);pool.submit(new AsynDomain());

九. 对HTTPClient的支持

目前TLog对于Httpclient客户端的支持是有侵入性的,对于请求端需要加入拦截器,把日志标签信息记录用过Http Header传递到服务器。

HttpClient 4.X 添加拦截器TLogHttpClientInterceptor

String url = "http://127.0.0.1:2111/hi?name=2323";
CloseableHttpClient client = HttpClientBuilder.create()
            .addInterceptorFirst(new TLogHttpClientInterceptor())
            .build();
HttpGet get = new HttpGet(url);
try {
    CloseableHttpResponse response = client.execute(get);
    HttpEntity entity  = response.getEntity();
    log.info("http response code:{}", response.getStatusLine().getStatusCode());
    log.info("http response result:{}",entity == null ? "" : EntityUtils.toString(entity));
} catch (IOException e) {
    e.printStackTrace();
}

HttpClient 5.X 添加拦截器TLogHttpClient5Interceptor

String url = "http://127.0.0.1:2111/hi?name=2323";
CloseableHttpClient client = HttpClientBuilder.create()
            .addInterceptorFirst(new TLogHttpClient5Interceptor())
            .build();
HttpGet get = new HttpGet(url);
try {
    CloseableHttpResponse response = client.execute(get);
    HttpEntity entity  = response.getEntity();
    log.info("http response code:{}", response.getStatusLine().getStatusCode());
    log.info("http response result:{}",entity == null ? "" : EntityUtils.toString(entity));
} catch (IOException e) {
    e.printStackTrace();
}

十. 对mq中间件的支持

目前Tlog对于消息中间件的支持是有侵入性的,暂时无法做到完全无侵入。

对于客户端,你只需要用TLogMqWrapBean包装你的业务bean就可以了。

TLogMqWrapBean<BizBean> tLogMqWrap = new TLogMqWrapBean(bizBean);mqClient.send(tLogMqWrap);

对于消费者端,你需要这么做:

//从mq里接受到tLogMqWrapBean
TLogMqConsumerProcessor.process(tLogMqWrapBean, new TLogMqRunner<BizBean>() {
    @Override
    public void mqConsume(BizBean o) {
        //业务操作
    }
});

十一. 对SpringCloud Gateway的支持

TLog从1.3.0开始,对spring cloud gateway也进行了支持

你只需在gateway的server项目中引入tlog-web-spring-boot-startertlog-gateway-spring-boot-starter模块即可

TLog会在gateway启动时进行自动装载适配。

如果进行了全需依赖导入,则无需再进行按需依赖导入

十二. 自动打印调用参数和时间

对于springboot应用来说,你只需在springboot的配置文件中作如下配置:

# 不配默认为falsetlog.enable-invoke-time-print=true

当你进行RPC调用时,会自动打印出参数信息和调用时间信息:

2020-12-01 19:20:07.768 [DubboServerHandler-127.0.0.1:30900-thread-2] INFO  c.y.tlog.dubbo.filter.TLogDubboInvokeTimeFilter - <0.1><7592057736843136> [TLOG]开始调用接口[DemoService]的方法[sayHello],参数为:["jack"]
2020-12-01 19:20:07.787 [DubboServerHandler-127.0.0.1:30900-thread-2] INFO  c.y.t.example.dubbo.service.impl.DemoServiceImpl - <0.1><7592057736843136> logback-dubbox-provider:invoke method sayHello,name=jack
2020-12-01 19:20:07.788 [Thread-14] INFO  c.y.tlog.example.dubbo.service.impl.AsynDomain - <0.1><7592057736843136> 这是异步方法哦
2020-12-01 19:20:07.788 [Thread-14] INFO  c.y.tlog.example.dubbo.service.impl.AsynDomain - <0.1><7592057736843136> 异步方法开始
2020-12-01 19:20:07.789 [Thread-14] INFO  c.y.tlog.example.dubbo.service.impl.AsynDomain - <0.1><7592057736843136> 异步方法结束
2020-12-01 19:20:07.795 [DubboServerHandler-127.0.0.1:30900-thread-2] INFO  c.y.tlog.dubbo.filter.TLogDubboInvokeTimeFilter - <0.1><7592057736843136> [TLOG]结束接口[DemoService]中方法[sayHello]的调用,耗时为:90毫秒

十三. 自定义TraceId生成器

TLog默认采用snowflake算法生成traceId,当然你也可以去更换traceId的生成算法。

定义自己的traceId生成类去继承TLogIdGenerator接口:

public class TestIdGenerator extends TLogIdGenerator {
   @Override
   public String generateTraceId() {
      return String.valueOf(System.nanoTime());
   }
}

然后在springboot的配置类里声明:

tlog.id-generator=com.yomahub.tlog.example.dubbo.id.TestIdGenerator

十四. SpringCloud的Openfeign

如果你的RPC是spring cloud,需要在spring xml里如下配置

<bean class="com.yomahub.tlog.feign.filter.TLogFeignFilter"/>
<bean class="com.yomahub.tlog.core.aop.AspectLogAop"/>

同时需要在spring mvc的xml里做如下配置

<mvc:interceptors>
  <bean class="com.yomahub.tlog.web.interceptor.TLogWebInterceptor" />
</mvc:interceptors>


欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢

5bd3b5b89985d76cd96d3d17bd07629c.png

已在知识星球更新源码解析如下:

38bdad1aa711c8c8ff26013346b26121.jpeg

21299ac636420e0e7d72934d011be408.jpeg

af853b03053b4ff1f79c520a7d491212.jpeg

ad4599f43582fc180221df2319f868b7.jpeg

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

提供近 3W 行代码的 SpringBoot 示例,以及超 6W 行代码的电商微服务项目。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值