springboot工程中使用slf4j+logback实现日志唯一编号

在某些项目中,特别是实时交易项目中,需要根据一个唯一的编号把某个交易事务的所有日志都查询出来,通过grep命令可以快速定位该笔交易发生异常的位置和原因。本文基于springboot+slf4j+logback来实现上述需求。

一、工程依赖

首先既然是springboot工程,那么对springboot的依赖配置肯定不能少,如下:

xml 复制代码
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.12.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

接下来就是配置log相关的依赖,如下:

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
</dependency>

上述依赖包含了对logback的依赖,如下图所示:

依赖准备好之后,接着我们一起完成日志输出配置。

二、日志输出配置

日志输出配置通常包含三部分:Level(日志输出级别)、Appender(输出到哪里)、Layout(输出格式)。logback中将Appender的作用放大了,除了指定输出位置之外,还能配置输出策略以及输出格式。这节只需要对appender的子元素pattern进行调整,增加输出%X{logId}。%X可以引用日志上下文的信息,这些信息是保存在MDC中的。示例配置如下:

xml 复制代码
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <!--格式化输出:%d表示日期,%X{logId}表示日志唯一编号 %thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{logId}] [%thread] %-5level %logger{60}[%L] - %msg%n</pattern>
    </encoder>
</appender>

logId的信息是何时怎么保存到MDC(Mapped Diagnostic Contexts)中的呢?请看下面的拦截器!

三、日志拦截器

先上拦截器代码:

java 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

/**
 * 日志拦截器组件,在输出日志中加上唯一编号
 *
 */
public class LogInterceptor extends HandlerInterceptorAdapter {
    Logger logger = LoggerFactory.getLogger(LogInterceptor.class);

    private final static String LOG_ID_KEY = "logId";

    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
        try {
            // 删除logId
            MDC.remove(LOG_ID_KEY);
        } catch (Exception e) {
            logger.warn(e.getMessage(), e);
        }
    }

    @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 {

        try {
            // 设置logId
            String logId = UUID.randomUUID().toString().replace("-", "");
            MDC.put(LOG_ID_KEY, logId);
            return true;
        } catch (Exception e) {
            logger.warn(e.getMessage(), e);
            return false;
        }
    }
}

MDC操作实质是将数据保存到ThreadLocal中,这样子就能够保证一个同步交易的所有日志都具有相同的日志编号。

上述我们只是定义了一个日志拦截器,我们还需要将该拦截器配置到容器中,并加入拦截器链,配置代码如下:

scala 复制代码
@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {

    /**
     * 把日志拦截器注入为bean
     *
     * @return
     */
    @Bean
    public HandlerInterceptor logInterceptor() {
        return new LogInterceptor();
    }

    /**
     * 注册拦截器
     *
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // addPathPatterns 用于添加拦截规则, 这里假设拦截 /url 后面的全部链接
        // excludePathPatterns 用户排除拦截
        registry.addInterceptor(logInterceptor()).addPathPatterns("/**");
        super.addInterceptors(registry);
    }
}

到此,springboot工程可以对每次接口调用产生的日志都标记唯一的编号。效果如下图所示:

上图对接口/like/{name}发起了两次请求,日志分别获得了两个编号:7222ff7a41d24fd39b88eb3cbc394aa3和84c49795fe2348108efa0ff172d78f38。请读者亲自尝试。

相关推荐
StockTV1 小时前
印度股票实时数据 NSE和BSE的实时行情、K 线及指数数据
java·开发语言·spring boot·python
橘子海全栈攻城狮2 小时前
【最新源码】养老院系统管理A013
java·spring boot·后端·web安全·微信小程序
敖正炀2 小时前
反模式与排查宝典:Spring Boot 自动配置与核心机制的常见陷阱
spring boot
直奔標竿2 小时前
Java开发者AI转型第二十六课!Spring AI 个人知识库实战(五)——联网搜索增强实战
java·开发语言·人工智能·spring boot·后端·spring
吴爃4 小时前
Spring Boot 项目在 K8S 中的打包、部署与运维发布实践
运维·spring boot·kubernetes
a8a3024 小时前
Laravel8.x新特性全解析
java·spring boot·后端
白露与泡影4 小时前
Spring Boot 完整流程
java·spring boot·后端
小鲁蛋儿5 小时前
Dynamic + ShardingSphere整合
spring boot·shardingsphere·dynamic
北风toto6 小时前
Spring Boot / Spring Cloud 配置文件加密详解:使用 jasypt-spring-boot 实现 ENC() 加密
spring boot·后端·spring cloud