使用MDC快速查詢應用接口全部執(zhí)行日志
引言
對于每一個開發(fā)者來說,查詢接口的執(zhí)行日志都是一個高頻率的操作,每當測試說接口有問題時,我們都需要去服務器或者日志系統(tǒng)上查報錯的原因。
一般情況下,我們會通過對應的關鍵字或者接口地址去查詢這個接口到底報了什么錯,但是這帶來一個問題,就是我們可能少打日志或者忘打某些關鍵字的日志,導致查詢記錄比較麻煩。
那么有沒有一種簡單高效的方法,即使我們在日志中不打印任何關鍵字,系統(tǒng)會自動生成一個關鍵字,讓我們一次性查詢出這個接口的所有l(wèi)og記錄呢?
MDC
MDC是日志門面框架SLF4J提供的一個類,可以提供在多線程情況下記錄日志的功能,log4j、logback、log4j2都有對這個類的實現(xiàn)。
從本質上來說,MDC可以看做一個ThreadLocal,由于其線程安全的特性,可以讓我們輕松安全的保存數(shù)據(jù)。
MDC主要的API有clear()、get()、put()、remove()方法等,簡潔的api讓我們使用上手基本沒有難度。
如何使用
1,修改日志打印格式
以日志框架logback為例,在logback.xml中,找到日志打印規(guī)則的配置,添加**-%X{reqId}** 屬性,其中reqId可以任意指定,你寫其他的屬性也可以,博主這里演示指定為reqId。
<encoder> <pattern>-%X{reqId} %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %file:%line %msg%n</pattern> <charset>UTF-8</charset> </encoder>
2,添加過濾器MDCFilter
ps.使用攔截器也可以,效果是一樣的。對每個接口 做攔截。
@Component @AllArgsConstructor @Order(Ordered.HIGHEST_PRECEDENCE) @Slf4j public class MDCFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { try { //給每個請求接口生成一個requestId String requestId = RandomUtil.randomNumbers(10); //這里的reqId就是上面配置的,要保持一致 MDC.put("reqId", "reqId:" + requestId); chain.doFilter(request, response); } finally { MDC.clear(); } } @Override public void destroy() { } }
經過簡單的兩步就配置好了,下面我們看一下效果。
@PostMapping(value = "/mdcTest") public ResponseEntity<Object> mdcTest(String id, String name) { log.info("測試日志打印,id={},name={}", id, name); log.info("測試日志打印1"); log.info("測試日志打印2"); log.info("測試日志打印3"); log.info("測試日志打印4"); return ResponseEntity.ok().build(); }
每一行日志都有一個關鍵字reqId:9723829830,這樣我們查詢日志時只需要查詢關鍵字9723829830就可以直接查出來這個接口所有的執(zhí)行記錄了。
如果想更方便的話,也可以把這個關鍵字直接輸出到每一個接口的響應頭或者響應體中。
進階使用
對于普通的web應用我們可以直接攔截每個接口,自動生成一個請求id,那么對于微服務項目,一個接口可能會產生很多服務的調用,那如何一次性查出來所有系統(tǒng)內的日志呢?
對于日志的搜集本文暫不考慮,咱們先說如何做請求id的傳遞。
其實也很簡單,當我們有多個系統(tǒng)間的調用時,把reqId放到request的header中進行傳遞,然后下游系統(tǒng)獲取這個id就可以了。
比如下方的攔截器:
public class LogInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //如果有上層調用就從header中取出上層的ID String traceId = request.getHeader("reqId"); if (traceId == null) { //如果沒有,就生成一個默認的 traceId = RandomUtil.randomNumbers(10); } MDC.put("reqId", traceId); 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.remove("reqId"); } }
MDC存在的一些問題
我們在上文說過,MDC的本質是ThreadLocal,它會把數(shù)據(jù)都綁定到當前線程上。但是當我們使用多線程的時候,就會帶來一個數(shù)據(jù)丟失的問題。
所以,我們需要進行線程間的數(shù)據(jù)傳遞,保證MDC數(shù)據(jù)不丟失。
以線程池傳遞數(shù)據(jù)為例,ThreadPoolTaskExecutor提供了一個taskDecorator裝飾器,通過這個屬性,我們就可以實現(xiàn)屬性的傳遞。
首先,定義一個MDCContextDecorator,
public class MDCContextDecorator implements TaskDecorator { @Override public Runnable decorate(Runnable runnable) { Map<String,String> previous = MDC.getCopyOfContextMap(); return () -> { try { if (previous != null) { MDC.setContextMap(previous); } runnable.run(); } finally { MDC.clear(); } }; } }
然后設置線程池的taskDecorator屬性,
public ThreadPoolTaskExecutor executor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(20); //...其他屬性 //設置線程屬性的自動傳遞 executor.setTaskDecorator(new MDCContextDecorator()); return executor; }
最后
MDC的使用其實很簡單,對于我們查詢日志也很有幫助,應用也算是非常廣泛了。有興趣的同學也可以去看一下它的內部實現(xiàn),代碼也并不復雜。
以上就是使用MDC快速查詢應用接口全部執(zhí)行日志的詳細內容,更多關于MDC查詢接口執(zhí)行日志的資料請關注腳本之家其它相關文章!
相關文章
java對象數(shù)組實現(xiàn)學生信息管理系統(tǒng)
這篇文章主要為大家詳細介紹了java對象數(shù)組實現(xiàn)學生信息管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-06-06http協(xié)議進階之Transfer-Encoding和HttpCore實現(xiàn)詳解
這篇文章主要給大家介紹了http協(xié)議之Transfer-Encoding和HttpCore實現(xiàn)的相關資料,文中介紹的非常詳細,相信對大家具有一定的參考價值,需要的朋友們下面來一起看看吧。2017-04-04Java使用分治算法實現(xiàn)排序數(shù)索引功能示例【二分搜索】
這篇文章主要介紹了Java使用分治算法實現(xiàn)排序數(shù)索引功能,結合具體實例形式分析了java分治算法進行排序索引的相關操作技巧,需要的朋友可以參考下2017-09-09