目錄html
一句話歸納:logback 在實現了基本的日誌輸出到文件功能後,在企業實踐中,還會有其它的進階需求,本文對logback的進階使用進行描述。java
上一篇文章《springboot+logback 日誌輸出企業實踐(上)》對 logback 的使用及配置進行描述,並實現按日誌級別輸出到獨立文件功能。但在企業實踐中,還會有其它的需求,如須要在多環境下使用不一樣日誌級別,日誌輸出性能低怎麼處理,還有分佈式系統如何追蹤請求日誌等等,對於這些需求,logback 有提供相應的功能,本文將對這幾種需求的實現進行講解。具體有以下內容:git
如需看源碼,本文示例工程地址:https://github.com/mianshenglee/my-example/tree/master/springboot-logback-demo
github
logback 官方文檔指出,強烈建議啓用 logback 狀態數據的輸出,將會在很大程度上幫助咱們診斷 logback 相關問題。經過這些狀態數據,能夠知道 logback 配置文件加載狀況,配置中對應的 appender,logger的裝載狀況等。啓用狀態數據輸出有兩種方式:web
debug="true"
OnConsoleStatusListener
。以下:<!-- 輸出logback的自己狀態數據 --> <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
注意,兩者選其一便可,此處的 debug 與配置文件中的日誌級別沒有關係,只用於表示輸出狀態數據。spring
本示例中,使用第二種方式(添加 statusListener
元素),添加後,輸出內容以下所示:api
按以前的 logback 配置,日誌輸出到文件是同步輸出的,即每次輸出都會直接寫IO到磁盤文件,從而產生阻塞,形成沒必要要的性能損耗。固然,對於通常的應用,影響不大,但對於高併發的應用,仍是有必要對性能進行優化的。logback 提供了日誌異步輸出的 AsyncAppender。 異步輸出日誌的方式很簡單,添加一個基於異步寫日誌的appender
,並指向原先配置的appender
便可 。見如下配置:springboot
<!-- 異步輸出 --> <appender name="ASYNCDEBUG" class="ch.qos.logback.classic.AsyncAppender"> <!-- 默認若是隊列的80%已滿,則會丟棄TRACT、DEBUG、INFO級別的日誌,若要保留所有日誌,設置爲0 --> <discardingThreshold>0</discardingThreshold> <!-- 更改默認的隊列的深度,該值會影響性能.默認值爲256 --> <queueSize>1024</queueSize> <!-- 添加附加的appender,最多隻能添加一個 --> <appender-ref ref="DEBUGFILE"/> <includeCallerData>true</includeCallerData> </appender> //INFO 結構同上,略 //WARN 結構同上,略 //ERROR 結構同上,略 <!-- 異步輸出關聯到root --> <root level="DEBUG"> <appender-ref ref="STDOUT"/> <appender-ref ref="ASYNCDEBUG" /> ...//略 </root>
AsyncAppender 對應須要設置的參數主要有 :併發
屬性名 | 類型 | 描述 |
---|---|---|
queueSize | int |
隊列的最大容量,默認爲 256 |
discardingThreshold | int |
默認,當隊列還剩餘 20% 的容量時,會丟棄級別爲 TRACE, DEBUG 與 INFO 的日誌,僅僅只保留 WARN 與 ERROR 級別的日誌。想要保留全部的事件,能夠設置爲 0 |
includeCallerData | boolean |
獲取調用者的數據相對來講比較昂貴。爲了提升性能,默認狀況下不會獲取調用者的信息。默認狀況下,只有像線程名或者 MDC 這種"便宜"的數據會被複制。設置爲 true 時,appender 會包含調用者的信息 |
maxFlushTime | int |
根據所引用 appender 隊列的深度以及延遲, AsyncAppender 可能會耗費長時間去刷新隊列。當 LoggerContext 被中止時, AsyncAppender stop 方法會等待工做線程指定的時間來完成。使用 maxFlushTime 來指定最大的刷新時間,單位爲毫秒。在指定時間內沒有被處理完的事件將會被丟棄。這個屬性的值的含義與 Thread.join(long) 相同 |
neverBlock | boolean |
默認爲 false,在隊列滿的時候 appender 會阻塞而不是丟棄信息。設置爲 true,appender 不會阻塞你的應用而會將消息丟棄 |
AsyncAppender 的實現方式是經過阻塞隊列( BlockingQueue
)來避免日誌直接輸出到文件,而是把日誌事件輸出到 BlockingQueue
中,而後啓動一個新的worker線程,主線程不阻塞,worker線程則從隊列中獲取須要寫的日誌,異步輸出到對應的位置。oracle
使用 springboot 進行應用開發,支持對多環境的配置支持,只須要按application-*.properties
格式添加配置文件,而後使用 spring.profiles.active
指定環境便可。一樣,日誌輸出,通常在開發環境,使用 DEBUG 級別,以便以檢查問題,而在生產環境,則只輸出 ERROR 級別的日誌。以下所示,profile定義開發環境爲 dev ,生產環境爲 prod:
<!-- 開發環境:debug級別--> <springProfile name="dev"> <root level="DEBUG"> <appender-ref ref="STDOUT"/> ...//略 </root> </springProfile> <!-- 生產環境:error級別--> <springProfile name="prod"> <root level="INFO"> <appender-ref ref="STDOUT"/> ...//略 </root> </springProfile>
上述配置是對 root 進行 設置(固然,其它元素也可使用)。通過此設置後,則會根據 spring.profiles.active
而決定使用指定日誌級別輸出。
其實 logback 還支持使用 if 元素,使用 if-then-else 的形式,結合 condition 屬性來實現條件處理。有興趣的讀者能夠看官方文檔說明 "Conditional processing of configuration files"
使用springboot開發分佈式應用,不少都微服務化,當請求過來,可能須要調用多個服務來完成請求動做。在查詢日誌時,特別是請求量大的狀況下,日誌多,很難找到對應請求的日誌,形成定位異常難,日誌難以追蹤等問題。針對此類問題,logback 提供了 MDC ( Mapped Diagnostic Contexts 診斷上下文映射 ),MDC可讓開發人員能夠在 診斷上下文 中放置信息,這些消息是內部使用了 ThreadLocal實現了線程與線程之間的數據隔離,管理每一個線程的上下文信息 。而在日誌輸出時,能夠經過標識符%X{key}
來輸出MDC中的設置的內容。所以,在分佈式應用在追蹤請求時,實現思路以下:
request-id
,以標識這次請求。request-id
到MDC中request-id
做爲 header 參數request-id
的輸出做爲標識request-id
經過攔截器,實如今請求前添加request-id
,並放到 MDC 中;請求完成後清除的動做。添加包 interceptor
存放攔截器類,類定義以下:
@Slf4j @Component public class RequestIdTraceInterceptor implements HandlerInterceptor { public static final String REQUEST_ID_KEY = "request-id"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { MDC.put(REQUEST_ID_KEY, getRequestId(request)); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { //把requestId添加到響應頭,以便其它應用使用 response.addHeader(REQUEST_ID_KEY, MDC.get(REQUEST_ID_KEY)); //請求完成,從MDC中移除requestId MDC.remove(REQUEST_ID_KEY); } public static String getRequestId(HttpServletRequest request) {...// 後面給出} }
此攔截器主要覆蓋 preHandle
及 afterCompletion
方法,分別請求前和請求完成後的處理。使用 MDC.put()
及 MDC.remove()
實現對MDC的寫入及清除操做。
在獲取 request-id
時,使用方法是 getRequestId()
,以下所示:
public static String getRequestId(HttpServletRequest request) { String requestId; String parameterRequestId = request.getParameter(REQUEST_ID_KEY); String headerRequestId = request.getHeader(REQUEST_ID_KEY); // 根據請求參數或請求頭判斷是否有「request-id」,有則使用,無則建立 if (parameterRequestId == null && headerRequestId == null) { log.debug("no request-id in request parameter or header"); requestId = IdUtil.simpleUUID(); } else { requestId = parameterRequestId != null ? parameterRequestId : headerRequestId; } return requestId; }
根據請求參數或請求頭判斷是否有「request-id」,有則使用,無則建立,建立的request-id
爲simpleUUID,以此做爲惟一標識。
添加 config
包用於存放配置文件。繼承 WebMvcConfigurer
實現 addInterceptors
來添加攔截器到 web 配置中:
@Configuration public class WebAppConfig implements WebMvcConfigurer { @Autowired RequestIdTraceInterceptor requestIdTraceInterceptor; /** * 添加攔截器 * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { //添加requestId registry.addInterceptor(requestIdTraceInterceptor); } }
logback 的 MDC 輸出是用%X{key}
來做標識符進行輸出,所以,修改 logback-spring.xml
文件,在輸出格式中添加 %X{request-id}
輸出,以下:
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %5level [%10thread] [%X{request-id}] %40.40logger{40} [%10method,%line] : %msg%n"/>
至此,MDC處理完畢,啓動應用,訪問其中的某一個接口,輸出以下(其中8e955ff61fa7494788f52891a4fdbc6a
便可 request-id
):
注意,示例代碼沒有給出調用其它服務時的處理,當調用時,從 MDC 中獲取
request-id
,而後把它做爲 header參數,實現request-id
的傳遞。這樣查詢日誌時,根據此id來追蹤就能夠了。
本篇文章針對springboot應用開發中,如何更好的使用 logback 解決日誌輸出的相關問題,主要包括 loback 狀態數據的輸出,使用異步解決日誌輸出性能問題,配置多環境下的日誌輸出以及使用MDC解決分佈式應用追蹤請求。但願能對你們有幫助。
本文中使用的示例代碼已放在個人github:https://github.com/mianshenglee/my-example/tree/master/springboot-logback-demo
,有興趣的同窗能夠pull代碼,結合示例一塊兒學習。
logback官網: http://logback.qos.ch
logback中文手冊: http://www.logback.cn/
logback github:https://github.com/qos-ch/logback
使用requestId在分佈式系統追蹤請求:https://juejin.im/post/5b51f85c5188251af91a7525
關注個人公衆號(搜索Mason技術記錄
),獲取更多技術記錄: