日誌--列印規範

大超Bing發表於2020-10-08

日誌

記錄應用系統日誌主要有三個原因: 記錄操作軌跡, 監控系統執行狀況, 回溯系統故障。

  • 記錄操作行為及操作軌跡資料
    可以資料化地分析使用者偏好,有助於優化業務邏輯,為使用者提供個性化的服務, 例如: 通過access.log 記錄使用者的操作頻度和跳轉連結,有助於分析使用者的後續行為。
  • 監控系統執行狀況
    是指對伺服器使用狀態, 如記憶體,CPU等使用情況;應用執行情況,如相應時間,QPS 等互動狀態; 應用錯誤資訊,如空指標,SQL 異常等的監控。
  • 回溯系統故障
    當系統發生線上問題時,完整的現場日誌有助於工程師快速定位問題。 例如,當系統記憶體溢位時,如果日誌系統記錄了問題發生現場的堆資訊,就可以通過這個日誌分析時什麼物件在大量產生並且沒有釋放記憶體, 回溯系統故障,從而定位問題。

日誌規範

推薦的日誌檔案命名方式為: appName_logType_logName.log 。其中,logType為日誌型別,推薦分類有 stats(統計),monitor(監控),visit(訪問)等; logName 為日誌描述。
這種命名的好處就是通過檔名就可以知道檔案屬於什麼應用,什麼型別,什麼目的,也有利於歸類查詢。例如,mppserver 應用中單獨監控時區轉換異常的日誌檔案定義為: mppserver_monitor_timeZoneConvert.log 。

日誌的級別

針對不同的場景,日誌被分為五種不同的級別,按照重要程度由低到高排序

  • DEBUG 級別日誌記錄對除錯程式有幫助的資訊。
  • INFO 級別日誌 用來記錄程式執行現場,雖然此處未發生錯誤,但是對排查其他錯誤具有指導意義。
  • WARN 級別日誌 也可以用來記錄程式執行現場, 但是更偏向於表明此處有出現潛在錯誤的可能。
  • ERROR 級別日誌 表明當前程式執行發生了錯誤,需要被關注,但是當前發生的錯誤,沒有影響系統的繼續執行。
  • FATAL 級別日誌 表明當前程式執行出現了嚴重的錯誤事件,並且將會導致應用程式中斷。
  1. 預先判斷日誌級別
    對DEBUG ,INFO 級別的日誌,必須使用條件輸出或者使用佔位符的方式列印。該約定綜合考慮了程式的執行效率和日誌列印需求。
// 使用條件判斷形式
if(logger.isDebugEnabled()){
	logger.debug("Percessing trade with id: "+ id + "and symbol: "+symbol);
}

// 使用佔位符形式
logger.debug("Processing trade with id: {} and symbol: {}", id, symbol);
  1. 避免無效日誌列印
    生產環境禁止輸出DEBUG 日誌且有選擇地輸出 INFO 日誌。
    使用 INFO ,WARN 級別來記錄業務行為資訊時,一定要控制日誌輸出量,以免磁碟空間不足,同時要為日誌檔案設定合理的生命週期,及時清理過期日誌。
    避免重複列印,務必在日誌配置檔案中設定 additivity=false, 示例:
<logger name="com.taobao.ecrm.member.config"  additivity = "false"> 
  1. 區別對待錯誤日誌
    WARN, ERROR 都是與錯誤有關的日誌級別,但不要一發生錯誤就籠統的輸出ERROR 級別日誌. 一些業務異常時可以通過引導重試就能恢復正常的, 例如 使用者輸入引數錯誤.在這種情況下,記錄日誌是為了在使用者諮詢時可以還原現場,如果輸出為ERROR 級別就表示一旦出現就需要人為介入, 這顯然是不合理的,所以,ERROR級別只記錄系統邏輯錯誤,異常或者違反重要的業務規則,其他錯誤都可以歸為 WARN 級別.

  2. 保證記錄內容完整
    日誌記錄的內容包括現場上下文資訊與異常堆疊資訊, 所以列印時需要注意以下兩點:
    (1) 記錄異常時一定要輸出異常堆疊, 例如 logger.error(“xxx”+e.getMessage(),e);
    (2) 日誌中如果輸出物件例項,要確保例項類重寫了 toString 方法,否則只會輸出物件的 hashCode值,沒有實際意義.

綜上所述, 記錄日誌時一定請思考三個問題:
① 日誌是否有人看
② 看到這條日誌能做什麼
③ 能不能提升問題排除效率

日誌框架

log4j, logback , jdk-logging, slf4j, commons-logging等框架;
日誌框架分為三大部分,包括日誌門面, 日誌介面卡, 日誌庫;
利用門面設計模式, 即 Facade 來進行解耦, 使日誌使用變得更加簡單. 如圖所示:

在這裡插入圖片描述

  1. 日誌門面
    門面設計模式是物件導向設計模式中的一種,日誌框架採用的就是這種模式, 類似於JDBC 的設計理念. 它只提供了一套介面規範,自身不負責日誌功能的實現, 目的是讓使用者不需要關注底層具體是哪個日誌庫來負責日誌列印及具體的使用細節等. 目前用得最為廣泛的日誌門面有兩種: slf4j 和 commons-logging

  2. 日誌庫
    它具體實現了日誌的相關功能, 主流的日誌庫有三個,分別是log4j, log-jdk, logback.
    在JDK1.4版引入了一個日誌庫 java.util.logging.Logger ,簡稱 log-jdk .
    logback 是最晚出現的, 它與log4j 出自同一個作者, 是log4j 的升級版且本身就實現了 slf4j的介面

  3. 日誌介面卡
    日誌介面卡分兩種場景:
    (1) 日誌門面介面卡, 因為slf4j 規範是後來提出的,在此之前的日誌庫是沒有實現 slf4j 的介面的, 例如 log4j; 所以,在工程裡使用 slf4j + log4j 的模式,就額外需要一個介面卡( slf4j-log4j12) 來解決介面不相容的問題
    (2) 日誌庫介面卡, 在一些老的工程裡, 一開始為了開發簡單而直接使用了日誌庫API 來完成日誌列印, 隨著時間的推移想將原來直接呼叫日誌庫的模式改為業界標準的門面模式( 例如 slf4j + logback 組合),就需要一個介面卡來完成從舊日誌庫的API 到slf4j 的路由

如果是新工程推薦使用 slf4j + logback 模式, 因為logback 自身實現了slf4j 的介面, 無需額外引入介面卡,另外 logback 是log4j 的升級版, 具備比 log4j 更多的優點.
可通過如下配置進行整合

<dependency>
	<groupid>org.slf4j</groupid>
	<artifactid>slf4j-api</artifactid>
	<version>${slf4j-api.version}</version>
</dependency>
<dependency>
	<groupid>ch.qos.logback</groupid>
	<artifactid>logback-core</artifactid>
	<version>${logback-core.version}</version>
</dependency>
<dependency>
	<groupid>ch.qos.logback</groupid>
	<artifactid>logback-classic</artifactid>
	<version>${logback-classic.version}</version>
</dependency>

完成了日誌框架的整合,再加上一個日誌配置檔案(如 logback.xml , log4j.xml等) , 並在工程啟動時載入,然後就可以進行日誌列印了.示例程式碼如下:

private static final Logger logger = LoggerFactory.getLogger(Abc.class) ;

logger 被定義為static 變數,是因為這個 logger 與當前類繫結, 避免每次都new 一個新物件, 造成資源浪費,甚至引發 OutOfMemoryError 問題.

相關文章