最近线上排查问题时候,发现请求太多导致日志错综复杂,没办法把用户在一次或多次请求的日志关联在一起,所以就利用SpringBoot+Logback手写了一个简单的链路追踪,下面详细介绍下。

一、实现原理

Spring Boot默认使用LogBack日志系统,并且已经引入了相关的jar包,所以我们无需任何配置便可以使用LogBack打印日志。

MDC(Mapped Diagnostic Context,映射调试上下文)是log4j和logback提供的一种方便在多线程条件下记录日志的功能。

实现思路是在一个请求开始时,将请求相关的上下文信息(例如客户ID、客户的IP地址、sessionId、请求参数等)添加到MDC,然后配置好logback-spring.xml,则Logback组件将会在每条日志中打印出存放到MDC的信息,从而实现一个ID贯穿用户的所有操作。

二、代码实战

新建一个spring boot项目spring-boot-log,按照下面步骤操作。

  1. 新建日志拦截器

日志拦截器在请求开始获取用户的sessionId,当然也可以生成一个UUID,生成后存放到MDC中。

SessionInterceptor代码如下:

/**
* 日志拦截器
* @Author: Java碎碎念
*
*/
public class SessionInterceptor extends HandlerInterceptorAdapter {
/**
* 会话ID
*/
private final static String SESSION_KEY = "sessionId"; @Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2, ModelAndView arg3) throws Exception {
} @Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception { // String token = UUID.randomUUID().toString().replaceAll("-","");
//本例测试使用sessionId,也可以使用UUID等
String token = request.getSession().getId();
MDC.put(SESSION_KEY, token);
return true;
} @Override
public void afterCompletion(HttpServletRequest arg0,
HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
// 删除
MDC.remove(SESSION_KEY);
}
}
  1. 新建配置类

新建InterceptorConfig,注册刚才的日志拦截器。

InterceptorConfig代码如下:

@Configuration
public class InterceptorConfig implements WebMvcConfigurer { @Bean
public SessionInterceptor getSessionInterceptor() {
return new SessionInterceptor();
} @Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getSessionInterceptor()).addPathPatterns("/*");
}
}
  1. 修改logback-spring.xml

配置logback-spring.xml,获取日志拦截器添加的sessionId并打印到日志中,配置文件中获取方式如下:

%X{sessionId}

本例中打印sessionId到控制台和文件,完整配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="log.base" value="./log/logback"/>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern> %date [%thread] [%X{sessionId}] %-5level %logger{80} - %msg%n
</pattern>
</encoder>
</appender> <appender name="logfile"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${log.base}.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${log.base}.%d{yyyy -MM-dd}.log.zip</FileNamePattern>
</rollingPolicy>
<encoder>
<pattern> %date [%thread] [%X{sessionId}] %-5level %logger{80} - %msg%n
</pattern>
</encoder>
</appender>
<logger name="com.sample" level="TRACE"/>
<root>
<level value="INFO"/>
<appender-ref ref="stdout"/>
<appender-ref ref="logfile"/>
</root>
</configuration>
  1. 添加controller

新建TestLogController,打印日志。

代码如下:

@RestController
public class TestLogController { Logger log = LoggerFactory.getLogger(getClass()); /**
* 测试登录
*/
@RequestMapping(value = "/testLogin")
public String testLogin() {
log.info("用户登录成功!");
return "ok";
} /**
* 测试下单
*/
@RequestMapping(value = "/testNewOrder")
public String testNewOrder() {
log.info("用户创建了订单!");
log.info("请求完成,返回ok!");
return "ok";
} /**
* 测试购买
*/
@RequestMapping(value = "/testPay")
public String testPay() {
log.info("用户付款!");
return "ok";
}
}

三、测试

打开浏览器连续访问接口testLogin、testNewOrder和testPay,模拟用户登录、下单、付款操作,控制台和文件中打印的日志中已经包含了sessonId信息,打印的结果如下:

[http-nio-8888-exec-1] [CB8E7DB250A31F2BE6C05B30633B9A95] INFO  com.example.springbootlog.controller.TestLogController - 用户登录成功!
[http-nio-8888-exec-2] [CB8E7DB250A31F2BE6C05B30633B9A95] INFO com.example.springbootlog.controller.TestLogController - 用户创建了订单!
[http-nio-8888-exec-2] [CB8E7DB250A31F2BE6C05B30633B9A95] INFO com.example.springbootlog.controller.TestLogController - 请求完成,返回ok!
[http-nio-8888-exec-3] [CB8E7DB250A31F2BE6C05B30633B9A95] INFO com.example.springbootlog.controller.TestLogController - 用户付款!

到此SpringBoot+Logback手写一个简单的链路追踪功能已经全部实现,有问题欢迎留言沟通哦!

完整源码地址: https://github.com/suisui2019/springboot-study

推荐阅读

1.SpringBoot中如何优雅的读取yml配置文件?

2.SpringBoot中如何灵活的实现接口数据的加解密功能?

3.SpringBoot中神奇的@Enable*注解?

4.Java中Integer.parseInt和Integer.valueOf,你还傻傻分不清吗?

5.SpringCloud系列-整合Hystrix的两种方式


限时领取免费Java相关资料,涵盖了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo/Kafka、Hadoop、Hbase、Flink等高并发分布式、大数据、机器学习等技术。

关注下方公众号即可免费领取:

利用SpringBoot+Logback手写一个简单的链路追踪的更多相关文章

  1. 手写一个简单的starter组件

    spring-boot中有很多第三方包,都封装成starter组件,在maven中引用后,启动springBoot项目时会自动装配到spring ioc容器中. 思考: 为什么我们springBoot ...

  2. 手写一个简单的ElasticSearch SQL转换器(一)

    一.前言 之前有个需求,是使ElasticSearch支持使用SQL进行简单查询,较新版本的ES已经支持该特性(不过貌似还是实验性质的?) ,而且git上也有elasticsearch-sql 插件, ...

  3. 手写一个简单到SpirngMVC框架

    spring对于java程序员来说,无疑就是吃饭到筷子.在每次编程工作到时候,我们几乎都离不开它,相信无论过去,还是现在或是未来到一段时间,它仍会扮演着重要到角色.自己对spring有一定的自我见解, ...

  4. 手写一个简单的HashMap

    HashMap简介 HashMap是Java中一中非常常用的数据结构,也基本是面试中的"必考题".它实现了基于"K-V"形式的键值对的高效存取.JDK1.7之前 ...

  5. 手写一个简单版的SpringMVC

    一 写在前面 这是自己实现一个简单的具有SpringMVC功能的小Demo,主要实现效果是; 自己定义的实现效果是通过浏览器地址传一个name参数,打印“my name is”+name参数.不使用S ...

  6. 如何利用C# Roslyn编译器写一个简单的代码提示/错误检查?

    OK, 废话不多说,这些天在写C#代码时突然对于IDE提示有了一些想法,之前也有了解过,不过并没有深入. 先看个截图: 一段再简单不过的代码了,大家注意看到 count 字段下面的绿色波浪线了吗,我们 ...

  7. (二)如何利用C# Roslyn编译器写一个简单的代码提示/错误检查?

    上一篇我们讲了如何建立一个简单的Roslyn分析项目如分析检查我们的代码. 今天我们主要介绍各个项目中具体的作用以及可视化分析工具. 还是这种截图,可以看到解决方案下一共有三个项目. Analyzer ...

  8. jquery 手写一个简单浮窗的反面教材

    前言 初学jquery写的代码,陈年往事回忆一下. 正文 介绍一下大体思路 思路: 1.需要控制一块区域,这块区域一开始是隐藏的. 2.这个区域需要关闭按钮,同时我需要写绑定事件,关闭的时候让这块区域 ...

  9. socket手写一个简单的web服务端

    直接进入正题吧,下面的代码都是我在pycharm中写好,再粘贴上来的 import socket server = socket.socket() server.bind(('127.0.0.1', ...

随机推荐

  1. Java门面模式

    一.简介 隐藏系统的复杂性,对外提供统一的访问入口,外部系统访问只通过此暴露出的统一接口访问.是一种结构型模式.封装子系统接口的复杂性,提供统一的对外接口,能够使子系统更加简单的被使用. 二.结构及使 ...

  2. Dart数据类型

    变量与常量: 变量: 使用var声明变量,可以赋予不同数据类型的值, 未初始化时默认值是null 使用final声明的变量只能被赋值一次 void main(){ var a; print(a); a ...

  3. Redis Cluster 原理相关说明

    背景 之前写的 Redis Cluster部署.管理和测试 和 Redis 5.0 redis-cli --cluster help说明 已经比较详细的介绍了如何安装和维护Cluster.但关于Clu ...

  4. angular之路由

    一.核心问题 路由要解决的核心问题是通过建立url和页面之间的对应的关系,使不同的页面可以通过不用的url来展示. 首先,当用户在浏览器上输入URL后,Angular将获取该URL并将其解析生成一个U ...

  5. intellj Idea git ignore文件的.idea不起作用解决

    问题描述: idea中使用git每次提交的时候都会选中项目目录下.idea目录,虽然设置了.ignore文件但是不起作用. 综合网上搜索结果,并完美解决,方法如下: 1.原因就是git已经关联追踪了这 ...

  6. BUUCTF刷题记录(Web方面)

    WarmUp 首先查看源码,发现有source.php,跟进看看,发现了一堆代码 这个原本是phpmyadmin任意文件包含漏洞,这里面只不过是换汤不换药. 有兴趣的可以看一下之前我做的分析,http ...

  7. 02-17 kd树

    目录 kd树 一.kd树学习目标 二.kd树引入 三.kd树详解 3.1 构造kd树 3.1.1 示例 3.2 kd树搜索 3.2.1 示例 四.kd树流程 4.1 输入 4.2 输出 4.3 流程 ...

  8. CSS3 入门到精通(一)

    CSS 用于控制网页的样式和布局. CSS3 是最新的 CSS 标准. 一. CSS3边框 实例 向 div 元素添加圆角: div { border:2px solid; border-radius ...

  9. 构建gitlab+Jenkins+harbor+kubernetes的DevOps持续集成持续部署环境

    构建gitlab+Jenkins+harbor+kubernetes的DevOps持续集成持续部署环境 整个环境的结构图. 一.准备工作 gitlab和harbor我是安装在kubernetes集群外 ...

  10. JVM垃圾回收算法详解

    前言 在JVM内存模型中会将堆内存划分新生代.老年代两个区域,两块区域的主要区别在于新生代存放存活时间较短的对象,老年代存放存活时间较久的对象,除了存活时间不同外,还有垃圾回收策略的不同,在JVM中中 ...