爲Logback日誌添加唯一追蹤ID

平常在測試的時候,不容易定位報錯信息,這個時候給日誌加上唯一的追蹤ID,查找日誌的時候就非常方便了。
像這樣:
在這裏插入圖片描述
每個請求打印的日誌都會有不同的ID,哪個請求出錯,根據ID來定位日誌就行了。這個ID還會返回給前端,前端發現報錯讓後臺找原因的話只需要給個ID,後臺開發人員就能根據


環境:

  • SpringBoot - 2.1.5.RELEASE
  • JDK8

一、創建過濾器

LogMDCFilter.java

public class LogMDCFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        String requestIdKey = "requestId";
        String requestId = UUID.randomUUID().toString();
        MDC.put(requestIdKey, requestId);

        String levelName = "logLevel";
        String value = servletRequest.getParameter(levelName);
        if (StringUtils.isNotBlank(value)) {
        	//org.slf4j.MDC
            MDC.put(levelName, value.toUpperCase());
        }

        try {
            filterChain.doFilter(servletRequest, servletResponse);
        } finally {
            MDC.remove(requestIdKey);
            MDC.remove(levelName);
        }
    }

    @Override
    public void destroy() {

    }
}

DebugLogTurboFilter.java

public class DebugLogTurboFilter extends TurboFilter {

    @Override
    public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) {
        int levelInt = level.toInt();
        String levelName = MDC.get("logLevel");
        if (StringUtils.isBlank(levelName) || "DEBUG,INFO,TRACE,WARN".indexOf(levelName) == -1) {
            return FilterReply.NEUTRAL;
        }
        if ((Level.TRACE.levelStr.equals(levelName) && levelInt >= Level.TRACE_INT)
                || (Level.DEBUG.levelStr.equals(levelName) && levelInt >= Level.DEBUG_INT)
                || (Level.INFO.levelStr.equals(levelName) && levelInt >= Level.INFO_INT)
                || (Level.WARN.levelStr.equals(levelName) && levelInt >= Level.WARN_INT)) {
            return FilterReply.ACCEPT;
        }
        return FilterReply.DENY;
    }
}

二、註冊過濾器

FilterConfiguration.java

@Configuration
public class FilterConfiguration {

    @Bean
    public FilterRegistrationBean logFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        //注入過濾器
        registration.setFilter(new LogMDCFilter());
        //攔截規則
        registration.addUrlPatterns("/*");
        //過濾器名稱
        registration.setName("logMDCFilter");
        //過濾器順序
        registration.setOrder(0);
        return registration;
    }
}

三、Logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%date{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}:%line - %msg %X{requestId}%n
            </pattern>
        </encoder>
    </appender>
	//上面的 DebugLogTurboFilter
    <turboFilter class="com.example.common.config.DebugLogTurboFilter">
    </turboFilter>

    <appender name="rollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>/home/logs/merchant/common-merchant.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>/home/logs/merchant/history/merchant.%d{yyyy-MM-dd HH}.%i.log</fileNamePattern>
            <maxHistory>30</maxHistory>
            <maxFileSize>3MB</maxFileSize>
        </rollingPolicy>
        <encoder>
            <pattern>%date{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}:%line - %msg %X{requestId}%n
            </pattern>
        </encoder>
    </appender>

    <!-- project default level -->
    <logger name="com.version" level="DEBUG"/>
    <logger name="org.springframework.web.servlet.handler" level="INFO"/>
    <logger name="com.example.user.mapper" level="DEBUG"/>
    <!--log4jdbc -->
    <logger name="org.springframework.jdbc.core.JdbcTemplate" level="INFO"/>

    <root level="INFO">
        <appender-ref ref="console"/>
        <appender-ref ref="rollingFile"/>
    </root>
</configuration>

注意配置中的{requestId}佔位符,這個就會替換爲生成的唯一日誌ID。

四、返回值封裝

ResponseModel.java

public class ResponseModel implements Serializable {

    private static final long serialVersionUID = -8972819161141262263L;

    @ApiModelProperty(value = "是否處理成功", name = "success", example = "true")
    private Boolean success;
    @ApiModelProperty(value = "返回碼", name = "code", example = "200")
    private Integer code;
    @ApiModelProperty(value = "處理消息", name = "msg", example = "處理成功")
    private String msg;
    @ApiModelProperty(value = "返回數據", name = "data", example = "{}")
    private Object data;
    private String requestId = MDC.get("requestId");
    }

代碼寫完了,前端每次請求,如果有返回值的話,都會收到唯一日誌ID,如圖:
在這裏插入圖片描述
可以根據requestId,通過此命令在日誌文件中查找對應的日誌

cat common-user.log | grep -C 10 f11819ea-6169-4c13-8032-979a88977575

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章