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。请读者亲自尝试。

相关推荐
悟空码字3 小时前
Spring Boot 整合 MongoDB 最佳实践:CRUD、分页、事务、索引全覆盖
java·spring boot·后端
皮皮林5512 天前
拒绝写重复代码,试试这套开源的 SpringBoot 组件,效率翻倍~
java·spring boot
用户908324602734 天前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
用户8307196840825 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解5 天前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解5 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记5 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者6 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840826 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解6 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端