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

相关推荐
qq_124987075319 小时前
基于Java Web的城市花园小区维修管理系统的设计与实现(源码+论文+部署+安装)
java·开发语言·前端·spring boot·spring·毕业设计·计算机毕业设计
VX:Fegn089519 小时前
计算机毕业设计|基于springboot + vue云租车平台系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
Chasmれ19 小时前
Spring Boot 1.x(基于Spring 4)中使用Java 8实现Token
java·spring boot·spring
汤姆yu19 小时前
2026基于springboot的在线招聘系统
java·spring boot·后端
计算机学姐20 小时前
基于SpringBoot的校园社团管理系统
java·vue.js·spring boot·后端·spring·信息可视化·推荐算法
Coder_Boy_20 小时前
基于SpringAI的在线考试系统-企业级教育考试系统核心架构(完善版)
开发语言·人工智能·spring boot·python·架构·领域驱动
java1234_小锋20 小时前
Java高频面试题:SpringBoot如何自定义Starter?
java·spring boot·面试
indexsunny21 小时前
互联网大厂Java面试实战:微服务与Spring生态技术解析
java·spring boot·redis·kafka·mybatis·hibernate·microservices
Coder_Boy_21 小时前
基于SpringAI的在线考试系统-整体架构优化设计方案(续)
java·数据库·人工智能·spring boot·架构·领域驱动
笨蛋不要掉眼泪21 小时前
RAG知识库核心API架构全解析:从文档加载到向量检索的完整流程
java·spring boot·redis·ai·架构