Logback 背景
Logback是由log4j創始人設計的另一個開源日志組件,官方網站:http://logback.qos.ch。它當前分為下面下個模塊:
- logback-core:其它兩個模塊的基礎模塊
- logback-classic:它是log4j的一個改良版本,同時它完整實現了slf4j API使你可以很方便地更換成其它日志系統如log4j或JDK14 Logging
- logback-access:訪問模塊與Servlet容器集成提供通過Http來訪問日志的功能
普通debug日志
SQL執行日志
Logback 配置案例
日志級別排序為:TRACE < DEBUG < INFO < WARN < ERROR
- %d:表示日期
- %n:換行
- %thread:表示線程名
- %level:日志級別
- %msg:日志消息
- %file:表示文件名
- %class:表示文件名
- %logger:Java類名(含包名,這里設定了36位,若超過36位,包名會精簡為類似a.b.c.JavaBean)
- %line:Java類的行號
注意:
%-4relative %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread][%X{TRACE_ID}] %-5level %logger{100}.%M\(%line\) - %msg%n
在logback中,%relative表示自應用程序啟動以來打印相對時間戳(以毫秒為單位). %-4只是元素的對齊方式.
案例
3452487 2021-08-03 15:19:36.940 [thread-monitor-daemon][] WARN com.xxxx.common.util.MonitorLogger.warn(27) - 發現超時線程notify-replay-consumer...
由于案例中是守護線程thread-monitor-daemon,所以不記錄鏈路ID。
對在系統設計的時候對于線程的命名規范也是有約束的
這里就不做詳細展開后續有機會會分享。
回歸正題比如下面的例子中記錄了請求的鏈路ID
19006989 2021-08-04 22:35:25.776 [http-nio-0.0.0.0-8010-exec-10][1fc8pebmgwukw863w2p342rp2936a3r157w0:0:] INFO com.xxx.framework.eureka.core.listener.EurekaStateChangeListener.listen(58) - 服務實例[XX-PAAS]注冊成功,當前服務器已注冊服務實例數量[3]
對于上圖中顯示的系統啟動時間、當前時間、當前線程、對應路徑按照logback官方配置就可以逐步完善對于的日志信息,但是對于鏈路ID的生成寫入就需要特殊處理。
鏈路ID設計
對于鏈路追蹤設計我個人比較喜歡兩種方案
第一種
在每一次請求中鏈路編號(traceId)、單元編號(spanId)都是通過HttpHeader的方式進行傳遞,日志的起始位置會主動生成traceId、spanId,而起始位置的Parent SpanId則是不存在的,值為null。
這樣每次通過restTemplate、Openfeign的形式訪問其他服務的接口時,就會攜帶起始位置生成的traceId、spanId到下一個服務單元。
第二種
在每一次請求中鏈路編號(traceId),沒經過一次微服務對于深度(Deep)加1
public static class ThreadTraceListener implements ThreadListener { @Override public void onThreadBegin(HttpServletRequest request) { String traceToken = ThreadLocalUtil.getTranVar(TRACE_ID); String fromServer = ThreadLocalUtil.getTranVar(FROM_SERVER); int deep; String traceId; if (StringUtils.isBlank(traceToken)) { traceId = IDGenerator.generateID(); deep = 0; traceToken = StringHelper.join(traceId, ":0"); } else { int index = traceToken.lastIndexOf(':'); traceId = traceToken.substring(0, index); deep = Integer.valueOf(traceToken.substring(index + 1)); } ThreadLocalUtil.setLocalVar(TRACE_ID, traceId); ThreadLocalUtil.setLocalVar(TRACE_DEEP, deep); ThreadLocalUtil.setTranVar(TRACE_ID, StringHelper.join(traceId, ":", deep + 1)); ThreadLocalUtil.setLocalVar(FROM_SERVER, fromServer); ThreadLocalUtil.setTranVar(FROM_SERVER, getCurrentServer()); MDC.put(TRACE_ID, StringHelper.join(traceToken, ":", fromServer)); } @Override public void onThreadEnd(HttpServletRequest request) { MDC.remove(TRACE_ID); } }
針對請求攔截
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { long startTime = System.currentTimeMillis(); // 從Header中裝載傳遞過來的變量 Map<String, Object> tranVar = new HashMap<String, Object>(); Enumeration<String> headers = request.getHeaderNames(); while (headers.hasMoreElements()) { String key = headers.nextElement(); if (!StringUtils.isEmpty(key) && key.startsWith(ThreadLocalUtil.TRAN_PREFIX)) { tranVar.put(key.substring(ThreadLocalUtil.TRAN_PREFIX.length()), request.getHeader(key)); } } ThreadLocalHolder.begin(tranVar, request); try { if (isGateway) { response.addHeader("X-TRACE-ID", TraceUtil.getTraceId()); } // 檢查RPC調用深度 checkRpcDeep(request, response); // 業務處理 chain.doFilter(request, response); // 記錄RPC調用次數 logRpcCount(request, response); } catch (Throwable ex) { // 錯誤處理 Response<?> result = ExceptionUtil.toResponse(ex); Determine determine = ExceptionUtil.determineType(ex); ExceptionUtil.doLog(result, determine.getStatus(), ex); response.setStatus(determine.getStatus().value()); response.setCharacterEncoding("UTF-8"); response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); response.getWriter().write(JsonUtil.toJsonString(result)); } finally { try { doMonitor(request, response, startTime); if (TraceUtil.isTraceLoggerOn()) { log.warn(StringHelper.join( "TRACE-HTTP-", request.getMethod(), " URI:", request.getRequestURI(), ", dt:", System.currentTimeMillis() - startTime, ", rpc:", TraceUtil.getRpcCount(), ", status:", response.getStatus())); } else if (log.isTraceEnabled()) { log.trace(StringHelper.join(request.getMethod(), " URI:", request.getRequestURI(), ", dt:", System.currentTimeMillis() - startTime, ", rpc:", TraceUtil.getRpcCount(), ", status:", response.getStatus())); } } finally { ThreadLocalHolder.end(request); } } }
到此這篇關于微服務分布式架構實現日志鏈路跟蹤的方法的文章就介紹到這了,更多相關微服務分布式架構日志鏈路跟蹤內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://mp.weixin.qq.com/s/s_dVunSq9bkJGeRVN9qRaA