版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/catoop/article/details/71713232
我們先回想這樣一個場景:
在Java開發中,因為業務需求肯定會輸出很多日志。在出現問題時,根據輸出的日志分析問題。
對于一個通路量很大的網站來說,日志的輸出速度是很快的,同樣的代碼方法被同時調用是很正常的。
那麼現在問題來了,我們如何從日志中來區分每一個會話的日志呢?就是我們發現了一個異常,如何知道在這個異常之前對應的一些列日志是什麼呢?
我們都知道,不管是普通背景代碼執行(定時任務這種)還是使用者的HTTP請求,系統都會為開啟每一個獨立的線程來執行。
意思就是:10個使用者瞬間同時通路了同一個請求,那麼Tomcat就會配置設定10個不同的線程來分别執行每個請求。
思路來了:我們隻需要在輸出日志的時候,将每個線程的ID同時輸出出來即可。這樣我們首先要保證每個線程的ID是唯一的。logback日志本身就支援輸出線程名稱,使用這個是不行的,因為現在都是線程池,同一個線程是會被不同時間的多次請求公用的。
sl4j 提供的一個工具類
MDC
,支援 logback和log4j,其作用就是可以讓你放入一些變量值到日志中并輸出。
下面看一下片段代碼:
線上程執行第一行代碼之前放入變量值
MDC.put("ThreadID", java.util.UUID.randomUUID().toString().replaceAll("-", "").toUpperCase());
然後在 (以logback為例) 中使用通配符就可以輸出這個值,配置片段如下:
注意留意其中的
{ThreadID}
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
%d|%thread|%X{ThreadID}|%-5level|%logger{65}|%msg%n
</pattern>
</encoder>
現在又有一個問題,如何讓線程在執行第一行(相對)代碼的時候使用 MDC.put 加入變量值呢?
使用 Filter、MVC 攔截器(Interceptor)。
特别注意:在開始的地方加入ID,同樣需要線上程結束的時候删除該ID,因為線程線上程池裡,如果不在執行完業務代碼後删除ThreadID,那麼該線程下次被使用的時候ThreadID還是存在的,就達不到我們預期的效果了。
不多說了,下面給出執行個體代碼:
我沒有使用Filter,使用的是HandlerInterceptor,道理一樣的。因為我嫌Filter 攔截範圍太大,什麼都攔截還要自己判斷處理(這個根據自己實際需求選擇吧)。
/**
* 日志攔截器
*/
public class LogInterceptor implements HandlerInterceptor {
/**
* 會話ID
*/
private final static String SESSION_TOKEN_KEY = "sessionTokenId";
private static final transient Logger logger = LoggerFactory.getLogger(LogInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 放SessionId
String token = java.util.UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
MDC. put(SESSION_TOKEN_KEY, token);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// 其他邏輯代碼
// 最後執行MDC删除
MDC. remove(SESSION_TOKEN_KEY);
}
}
logback.xml 配置檔案中的部分代碼,僅供參考。
<appender name="front"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logger.basedir}/front.log</file>
<rollingPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${logger.basedir}/front.%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
%d|%thread|%X{sessionTokenId}|%-5level|%logger{65}|%msg%n
</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>DENY</onMatch>
<onMismatch>ACCEPT</onMismatch>
</filter>
</appender>
logback 還内置了過濾器,比如 MDCInsertingServletFilter,說了這麼多都不如直接看官網資料,官網位址:
https://logback.qos.ch/manual/mdc.html#autoMDC