最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

微服務(wù)分布式架構(gòu)實現(xiàn)日志鏈路跟蹤的方法

 更新時間:2021年08月20日 10:06:23   作者:碼農(nóng)架構(gòu)  
在現(xiàn)有的系統(tǒng)中,由于大量的其他用戶/其他線程的日志也一起輸出穿行其中導(dǎo)致很難篩選出指定請求的全部相關(guān)日志。那我們?nèi)绾蝸硖幚砟??帶著這個問題一起通過本文學(xué)習(xí)下吧

Logback 背景

Logback是由log4j創(chuàng)始人設(shè)計的另一個開源日志組件,官方網(wǎng)站:http://logback.qos.ch。它當(dāng)前分為下面下個模塊:

  • logback-core:其它兩個模塊的基礎(chǔ)模塊
  • logback-classic:它是log4j的一個改良版本,同時它完整實現(xiàn)了slf4j API使你可以很方便地更換成其它日志系統(tǒng)如log4j或JDK14 Logging
  • logback-access:訪問模塊與Servlet容器集成提供通過Http來訪問日志的功能

普通debug日志

SQL執(zhí)行日志

Logback 配置案例

日志級別排序為:TRACE < DEBUG < INFO < WARN < ERROR

  • %d:表示日期
  • %n:換行
  • %thread:表示線程名
  • %level:日志級別
  • %msg:日志消息
  • %file:表示文件名
  • %class:表示文件名
  • %logger:Java類名(含包名,這里設(shè)定了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表示自應(yīng)用程序啟動以來打印相對時間戳(以毫秒為單位). %-4只是元素的對齊方式.

案例

3452487 2021-08-03 15:19:36.940 [thread-monitor-daemon][] WARN  com.xxxx.common.util.MonitorLogger.warn(27) - 發(fā)現(xiàn)超時線程notify-replay-consumer...

由于案例中是守護(hù)線程thread-monitor-daemon,所以不記錄鏈路ID。

對在系統(tǒng)設(shè)計的時候?qū)τ诰€程的命名規(guī)范也是有約束的


這里就不做詳細(xì)展開后續(xù)有機會會分享。
回歸正題比如下面的例子中記錄了請求的鏈路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) - 服務(wù)實例[XX-PAAS]注冊成功,當(dāng)前服務(wù)器已注冊服務(wù)實例數(shù)量[3]

對于上圖中顯示的系統(tǒng)啟動時間、當(dāng)前時間、當(dāng)前線程、對應(yīng)路徑按照logback官方配置就可以逐步完善對于的日志信息,但是對于鏈路ID的生成寫入就需要特殊處理。

鏈路ID設(shè)計

對于鏈路追蹤設(shè)計我個人比較喜歡兩種方案

第一種

在每一次請求中鏈路編號(traceId)、單元編號(spanId)都是通過HttpHeader的方式進(jìn)行傳遞,日志的起始位置會主動生成traceId、spanId,而起始位置的Parent SpanId則是不存在的,值為null。

這樣每次通過restTemplate、Openfeign的形式訪問其他服務(wù)的接口時,就會攜帶起始位置生成的traceId、spanId到下一個服務(wù)單元。

 第二種

 

在每一次請求中鏈路編號(traceId),沒經(jīng)過一次微服務(wù)對于深度(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調(diào)用深度
      checkRpcDeep(request, response);
      // 業(yè)務(wù)處理
      chain.doFilter(request, response);
      // 記錄RPC調(diào)用次數(shù)
      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);
      }
    }
}

到此這篇關(guān)于微服務(wù)分布式架構(gòu)實現(xiàn)日志鏈路跟蹤的方法的文章就介紹到這了,更多相關(guān)微服務(wù)分布式架構(gòu)日志鏈路跟蹤內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java接入DeepSeek的保姆級教程(適合新手)

    Java接入DeepSeek的保姆級教程(適合新手)

    對于目前的DeepSeek大家應(yīng)該都不是很陌生,目前也是最流行的一款A(yù)I軟件了,所以為了讓我們開發(fā)更全面,能夠在自己的項目中融入AI那就會很全面了,所以這次的文章,將模擬一個基礎(chǔ)案例,可以在這個基礎(chǔ)案例迭代實現(xiàn)出你自己的AI,需要的朋友可以參考下
    2025-02-02
  • Java基礎(chǔ)第四篇 封裝與接口

    Java基礎(chǔ)第四篇 封裝與接口

    本文將對介紹Java 的封裝與接口進(jìn)行介紹,將要封裝(encapsulation)對象的成員(成員包括數(shù)據(jù)成員和方法),從而只允許從外部調(diào)用部分的成員。利用封裝,我們可以提高對象的易用性和安全性。想具體了解的小伙伴請參考下面文章的內(nèi)容
    2021-09-09
  • spring boot加入攔截器Interceptor過程解析

    spring boot加入攔截器Interceptor過程解析

    這篇文章主要介紹了spring boot加入攔截器Interceptor過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-10-10
  • Java整合mybatis實現(xiàn)過濾數(shù)據(jù)

    Java整合mybatis實現(xiàn)過濾數(shù)據(jù)

    這篇文章主要介紹了Java整合mybatis實現(xiàn)過濾數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-01-01
  • 使用BindingResult 自定義錯誤信息

    使用BindingResult 自定義錯誤信息

    這篇文章主要介紹了使用BindingResult 自定義錯誤信息,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 解決Spring國際化文案占位符失效問題的方法

    解決Spring國際化文案占位符失效問題的方法

    本篇文章主要介紹了解決Spring國際化文案占位符失效問題的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • Java中Map接口使用以及有關(guān)集合的面試知識點匯總

    Java中Map接口使用以及有關(guān)集合的面試知識點匯總

    在java面試過程中,Map時常會被作為一個面試點來問,下面這篇文章主要給大家介紹了關(guān)于Java中Map接口使用以及有關(guān)集合的面試知識點匯總的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • 關(guān)于使用Lambda表達(dá)式簡化Comparator的使用問題

    關(guān)于使用Lambda表達(dá)式簡化Comparator的使用問題

    這篇文章主要介紹了關(guān)于使用Lambda表達(dá)式簡化Comparator的使用問題,文中圖文講解了Comparator對象的方法,需要的朋友可以參考下
    2023-04-04
  • Java enum關(guān)鍵字不識別的快速解決辦法

    Java enum關(guān)鍵字不識別的快速解決辦法

    這篇文章主要介紹了Java enum關(guān)鍵字不識別的快速解決辦法,非常不錯,具有參考借鑒價值,感興趣的朋友一起看看吧
    2016-09-09
  • 詳解Spring 基于 Aspect 注解的增強實現(xiàn)

    詳解Spring 基于 Aspect 注解的增強實現(xiàn)

    本篇文章主要介紹了詳解Spring 基于 Aspect 注解的增強實現(xiàn),非常具有實用價值,需要的朋友可以參考下
    2017-04-04

最新評論

衢州市| 尼木县| 盖州市| 舟曲县| 盐津县| 噶尔县| 班戈县| 拉萨市| 铁岭市| 锡林郭勒盟| 宁国市| 略阳县| 安龙县| 南漳县| 津市市| 华容县| 太仆寺旗| 平安县| 乌拉特前旗| 梅河口市| 康乐县| 扶沟县| 无棣县| 清流县| 库车县| 乃东县| 方正县| 三门县| 深州市| 浮梁县| 鹿泉市| 扶绥县| 开原市| 高台县| 宣恩县| 鄯善县| 内黄县| 密云县| 怀柔区| 西吉县| 大同市|