JAVA-日誌的異步收集輸出

記錄一下最近做的一個Web項目中的日誌問題

項目使用的日誌體系如下:使用sl4j作爲門面,log的實際實現是log4j

問題如下

  在請求高併發的情況下,推測日誌會出現串的情況。

舉個例子

  

public void echo() {
  log.info("Function begin");
  //...省略若干行代碼...
  log.info("Function running");
  //...省略若干行代碼...
  log.info("Function end");
}

如果多個請求調用方法echo,最終的輸出情況可能如下

  Thread-1: Function begin

  Thread-2: Function begin

  Thread-3: Function begin

  Thread-3: Function running

  Thread-1: Function running

  Thread-1: Function end

  Thread-2: Function running

  Thread-3: Function end

  Thread-2: Function end

不同線程輸出的日誌會串到一起,很亂

網上查找資料,很少有提這一塊的。基本上都是log4j2的異步輸出

可是log4j2的異步輸出只是先放到buf中,buf滿了再一次輸出。提高的是寫的性能,向buf中加入的順序仍不能保證,仍然會串。並不是用來解決目前的這個情況的

此外還有使用日誌分析工具的,在日誌輸出時加上某些標誌 如IP、用戶名、線程名等業務信息,之後使用日誌分析工具篩選。這樣做還有一個問題 就是每次使用需要手動拼接業務信息輸出。log4j倒是有ThreadContext做這個事情,每次設置ThreadContext輸出時自動加上。

But!!!我們用的是sl4j,sl4j是一個日誌門面,實現了統一的接口 後面可以去找各個實現,logback、jul、log4j等等。sl4j沒有ThreadContext這個東西,如果使用ThreadContext意味着什麼?我們可以洗洗睡了,sl4j的使用就沒有意義了

我的解決思路:

解題基礎是動態代理、反射、多線程,新建一個自定義handler,在這個handler中存放一個sl4j的Logger對象

通過反射獲取Logger的Method列表(最好是static的 不要重複獲取)

定義一個static Map用於存儲線程間的信息

在invoke中,對比method,如果當前調用的method在Logger的method列表中,那麼不要執行這個method,而是將method和參數args記錄下來(這裏可以創建一個類 專門存放這些信息)。

然後問題來了 都存放下來不執行 那麼我的日誌怎麼輸出呢?

所以要做一個判斷,根據線程ID從前面提到的map中查看一個標誌是否存在(標誌的作用後面就明白了)

如果不存在 獲取當前線程 caller,新建一個線程commitTask,並設置這個標誌(爲了不重複創建commitTask),在commitTask中執行caller.join(),在caller.join()後獲取前面攔截的所有method和arg信息,然後使用logger實例依次執行。

所以提交的關鍵在於這個commitTask,因爲執行了caller.join(),所以commitTask會等待線程caller執行完後再執行caller.join()之後的代碼。

自己做一個LoggerFactory,在這個LoggerFactory中實例化上面創建的handler,調用sl4j的LoggerFactory創建一個sl4j的Logger實例,將Logger實例賦值給handler對象,根據handler對象創建Logger接口(可以自己定義接口繼承sl4j的Logger接口,這樣可以添加commit等自定義方法)的動態代理,返回這個動態代理

上述思路的效果:

在一個線程的運行期間,所有logger調用的info、warn等日誌方法都不輸出而是依次記錄下來,在線程執行完畢後,自動按照記錄的順序一起輸出(記得加鎖)。這樣一次請求期間產生的log日誌會放在一起輸出 不會出現串的現象

PS:

1、這種思路有利有弊,需要根據實際情況自己取捨

2、上述思路還有漏洞,即在一次業務請求中,如果業務代碼中有多線程的部分,會因爲根據線程爲單位收集日誌而使一個業務流程中的幾個線程間的日誌無法一起輸出。

  解決:使用自己定義的ID替換線程ID,這裏和事務有點像,不開啓事務就使用線程ID作爲單位收集。開啓後根據業務ID爲單位收集日誌(但是這樣沒辦法自動輸出,需要添加方法手動調用輸出。如 commit)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章