談談日誌的最佳實踐

1.背景

日誌是咱們程序員的一個老生常談的話題,你可能天天都會聽到這個詞。想起我剛剛大學畢業的時候剛進入公司,正逢作一些部門業務交接,也就是其餘部門的服務交給咱們維護。記得沒交接多久,當時業務上微信公衆號相關功能就出現了不可用,當時負責這部分業務的同窗,排查問題及其艱難,整個鏈路一個日誌都沒打,就在入口處error日誌,連續上了好幾回線,加了好幾輪日誌,才把問題給定位住了。當時其餘部門也出現了另一個例子,日誌打得太多了,因爲業務訪問的量級,致使大量日誌打出,從而讓磁盤IO打滿,最後讓整個服務癱瘓。html

時間一晃就過了好幾年,可是上面這樣的問題在不一樣的公司,不一樣的部門都在上演着,不少開發人員爲了圖本身的一些方便,不重視日誌的使用,有時候若是一直是你維護也還行,出了問題因爲是你開發的,可能你一眼就能發現,但若是你的代碼交接出去,讓其餘人進行維護。平時也有一些公衆號的讀者會向我諮詢日誌使用的一些問題,因而我在這裏結合平時的一些使用經驗還有《阿里巴巴java開發手冊》,寫一下我認爲的日誌最佳實踐。java

2.最佳實踐

2.1 合理的級別劃分

在日誌系統中有6種級別來控制咱們日誌的輸出:程序員

  • TRACE: 在線調試,這個基本沒有使用過,比較雞肋。
  • DEBUG: 用於調試的日誌,若是信息不是很重要,只是在某些極端的場景纔會須要,那麼就可使用DEBUG。
  • INFO: INFO信息一般用於某些日誌輸出須要常態化,須要常用它,咱們排查一些業務問題常常也須要這部分信息,那麼你就可使用INFO。
  • WARNING:警告信息,一般用於一些已知的業務錯誤,這部分錯誤基本能處理。
  • ERROR: 錯誤信息,一般用於咱們沒法處理的異常或者錯誤,對於這部分應該使用ERROR。
  • FATAL: 致命的錯誤,表明這程序須要立刻終止,這個用得也不多,在業務使用中咱們也不會使用

雖然有6種日誌級別,可是咱們在真正的業務開發中須要關注的業務級別通常來講只有3種,TRACE和FATAL這兩個基本不會使用,DEBUG在一些基礎工具開發中咱們使用得比較多,由於這些基礎工具打印日誌是對於業務方來講是隱式打印,因此若是不是過重要的信息都須要使用debug。面試

對於ERROR和WARN這兩種日誌其實有挺多同窗都會混用,一般來講出現ERROR日誌會發各類提醒,好比短信這些,有一些同窗總是把全部錯誤都給打成ERROR,好比用戶沒有權限,用戶餘額不足等等,那麼不免少不了短信轟炸。其實這部分錯誤實際上是屬於咱們業務流程中的一部分,那麼其實應該使用WARN打印日誌就夠了。這裏給你們推薦一個好的方法,來處理這種狀況,咱們將全部的業務異常都繼承一個異常,在咱們的業務中是BizException,經過捕獲這部分異常去打WARN日誌。spring

try{
            // do something
        }catch (BizException e){
            LOG.warn("biz exception", e);
        }catch (Exception e){
            LOG.error("exception", e);
        }

固然這部分邏輯也能夠經過切面去處理,把異常信息儘可能塞入到exception中,統一去處理。微信

動態日誌級別調整

咱們上面講了日誌有6種級別,可是咱們對於一個日誌系統來講會有一個總體的級別的選擇,一般在業務中咱們會選擇info,也就是咱們輸出的日誌級別大於等於info就會被輸出到文件中。咱們想一想下面的兩種狀況:框架

  • 在某個業務中觸發了一個bug,大量的打出error日誌,不只影響機器的性能,而且還頻繁的發出錯誤短信。
  • 須要排查某個基礎工具中間件的問題,可是日誌級別是debug,這個時候須要從新上線修改日誌級別爲debug排查問題。

這兩種場景均可以利用動態日誌級別調整去解決,當大量出現error日誌的時候,能夠當即把日誌關閉,防止因爲打印日誌引起更多的問題。當須要排查一些debug級別的問題的時候,直接修改到debug輸出級別就能知足須要。異步

動態日誌級別調整的方法通常有下面幾種方法:spring-boot

  • 若是是spring-boot 1.5以後的版本,引入spring-boot-starter-actuator,經過http接口修改日誌級別。
  • 使用arthas,經過ognl修改,以下面代碼所示:
ognl -c 1be6f5c3 '@org.slf4j.LoggerFactory@getLogger("root").setLevel(@ch.qos.logback.classic.Level@DEBUG)'

2.2 合理的使用佔位符

有一道經典面試題,以下面代碼所示,你能找到這個代碼的問題在哪嗎?工具

LOG.info("a:" + a + "b" + b );

若是咱們日誌輸出級別是warn,那麼咱們的info日誌其實是不會打印的,可是裏面的字符串相加這個操做確實會被執行,因此爲了出現這個問題,以前不少人都會加一個級別判斷:

if (LOG.isInfoEnabled()){
            LOG.info("a:" + a + "b" + b );
        }

可是這麼寫,就會致使有點拖沓,因而就出現了佔位符的寫法:

LOG.info("a:{}, b:{}", a, b);

這裏一行代碼就完成,這裏要說明的是日誌服務並無作特別的事,佔位符的替換依舊是經過MessageFormat這個類去作的,也就是會一個一個的遍歷,因此在這裏惟品會java手冊上推薦若是是ERROR的日誌,推薦使用上面的「a」 + a + 「b:」 + b這種模式,由於ERROR通常來講都須要每次打印,因此不須要擔憂白作了String的拼接。我我的以爲不必爲了佔位符的這點性能作太多的考慮,你info都使用了佔位符,通常來講info的日誌佔據了這個系統99%的日誌,只有那麼1%的error日誌,因此提高通常是不大的,而且反而會讓人產生錯用的可能。

異常不用佔位符

try{
           // do something
       }catch (Exception e){
           LOG.error("exception :{}", e);
       }

不少人在打印異常的時候都會像上面那樣去寫,佔位符填充的時候其實取的就是toString方法,那麼若是直接調用異常的toString就會致使異常的堆棧信息丟失,從而加大咱們排查問題的難度,因此這裏要強調若是打印異常不能直接用佔位符,而是直接寫在末尾,日誌打印的時候會自動打印出一場的堆棧信息。

2.3 合理的選擇日誌輸出方式

日誌輸出的模式分爲兩種:同步和異步模式。咱們在業務中通常來講會選擇異步模式,在log4j2中異步模式分爲兩種:

  • AsyncAppender:使用ArrayBlockingQueue保存異步日誌,而後使用一個異步線程輸出。
  • AsyncLogger:使用Disruptor框架保存日誌,而後使用一個異步線程輸出。

對於Disruptor以前寫過一篇文章: 詳解Disruptor,這是一個高性能隊列,若是咱們在log4j2中使用Dirsruptor會增長日誌輸出吞吐量。可是Disruptor的日誌輸出模式,咱們通常用得比較少,以前在美團的時候由於用Disruptor輸出日誌,形成了cpu打滿的狀況,因此Disruptor輸出日誌在有些地方是被禁止使用的。通常來講AsyncAppender就能知足咱們的使用了。

2.4 合理的保存日誌

當咱們思考清楚日誌的輸出方式以後,咱們就能夠考慮如何保存日誌,由於咱們的磁盤空間不是無限的,咱們就須要考慮日誌的過時刪除,阿里巴巴java開發手冊中明確的強制要求咱們至少保存15天日誌,對於用戶的敏感操做和重要的日誌,須要6個月的保存時間,在log4j2中也能夠經過下面的配置進行日誌刪除操做:

<DefaultRolloverStrategy max="30">
				<Delete basePath="${LOG_HOME}/" maxDepth="2">
					<IfFileName glob="*.log" />
					<IfLastModified age="30d" />
				</Delete>
			</DefaultRolloverStrategy>

這裏就表明這最多保存30天的日誌,固然也能夠在機器上面配置個定時任務進行刪除。

有時候咱們的機器比較多當咱們排查某個問題的時候,最原始的方法咱們會一個一個的機器上去看,後面咱們又開始用polysh,只須要在一臺機器上面執行命令,其餘機器就會自動執行,可是這樣仍是有點不方便,因此後面又出來了ELK,咱們使用Logstash收集全部的日誌而後存儲到Elasticsearch,最後使用Kibana進行可視化界面分析。因此使用Elasticsearch存儲日誌也是一個不錯的辦法。

2.5 合理的輸出日誌

在咱們的系統中,若是輸出大量的無效日誌,會影響咱們的系統的性能,因此咱們的日誌的打印也須要進行思考,哪些地方對咱們有幫助,而不是一股腦的所有打出。

咱們在經過日誌排查問題的時候,一般會跨服務,有時候這個日誌的信息不是能很好的對應上,因此咱們這個時候就須要有一個東西去把他們給關聯上,在這裏的話就是traceId,咱們經過一個traceId就能夠拿到整個鏈路中的日誌信息,對咱們的日誌排查特別方便。

2.6 不要有敏感信息

在2018年的時候facebook數據泄密,那個時候整個互聯網忽然開始重視起來敏感信息的泄漏,在日誌系統中也是很容易泄密的,好比用戶的姓名,手機號等等。 若是打在日誌中,很容易被不法分子盜用信息,因此咱們在打印日誌的時候要特別注意敏感信息的問題,具體的日誌脫敏我以前也寫過一篇文章有興趣的能夠看一下。手把手教你如何設計日誌脫敏插件

2.7 合理的日誌劃分

有不少同窗把全部日誌都打在了同一個文件裏面,對於咱們的排查日誌信息的時候特別不方便,咱們能夠把日誌分紅多個文件,好比根據不一樣的中間件,http,rpc,mq等等均可以單獨搞成獨立的日誌文件,這下排查某個問題的時候就比較容易概括查找。

2.8 第三方工具

雖然講了這麼多日誌的一些規約,可是不可能很完美的每次都能打出咱們想要的結果,好比某些方法可能沒有加日誌,可是又要排查一些問題。這個時候能夠借用咱們的第三方工具,好比arthas,咱們使用arthas的不少命令好比watch,trace同樣能夠完成咱們日誌的功能,只是第三方工具不是萬能,他只能幫助咱們檢查即將出現的一些數據,歷史的數據仍是得靠咱們的日誌系統去保證。

總結

固然日誌的實踐優化不只僅上上面這些點,還有更多的場景須要結合實際業務去進行優化。這裏但願你們能使用好日誌,讓天下沒有難排查的問題!

若是你們以爲這篇文章對你有幫助,你的關注和轉發是對我最大的支持,O(∩_∩)O:

相關文章
相關標籤/搜索