日志打印的正確姿勢


一、使用SLF4J門面模式的日志框架

使用門面模式的日志框架,有利於維護和各個類的日志處理方式統一
  • 日志門面
    門面模式,也稱之為外觀模式,其核心為:外部與一個子系統的通信必須通過一個統一的外觀對象進行,使得子系統更易於使用。日志門面,是門面模式的一個典型的應用。

  • 為什么需要日志門面
    1、每一種日志框架都有自己單獨的API,要使用對應的框架就要使用其對應的API,這就大大的增加應用程序代碼對於日志框架的耦合性。
    2、為了解決這個問題,就是在日志框架和應用程序之間架設一個溝通的橋梁,對於應用程序來說,無論底層的日志框架如何變,都不需要有任何感知。只要門面服務做的足夠好,隨意換另外一個日志框架,應用程序不需要修改任意一行代碼,就可以直接上線。
    3、因此,日志門面的一個比較重要的好處——解耦。

在軟件開發領域有這樣一句話:計算機科學領域的任何問題都可以通過增加一個間接的中間層來解決。而門面模式就是對於這句話的典型實踐。

二、打日志的正確方式

什么時候應該打日志?
  • 當你遇到問題的時候,只能通過debug功能來確定問題,你應該考慮打日志,良好的系統,是可以通過日志進行問題定位的

  • 當你碰到if…else 或者 switch這樣的分支時,要在分支的首行打印日志,用來確定進入了哪個分支

  • 經常以功能為核心進行開發,你應該在提交代碼前,可以確定通過日志可以看到整個流程

基本格式
  • 使用參數化信息的方式:
  logger.debug(" 財務保存線下收款單,收款單號:[{}] ,創建人:[{}]", receiptTaskNo, creatEmp);
  • 對於debug日志,必須判斷是否為debug級別后,才進行使用:
  if (logger.isDebugEnabled()) {
    logger.debug(" 財務保存線下收款單,收款單號:[{}] ,創建人:[{}]", receiptTaskNo, creatEmp);
   }
  • 不要進行字符串拼接,那樣會產生很多String對象,占用空間,影響性能。
    反例:
  logger.debug(" 財務保存線下收款單,收款單號:" + receiptTaskNo + "操作人:" + creatEmp);
  • 使用[]進行參數變量隔離
  logger.debug(" 財務保存線下收款單,收款單號:[{}] ,創建人:[{}]", receiptTaskNo, creatEmp);

這樣的格式寫法,可讀性更好,對於排查問題更有幫助。

三、通常使用的不同級別的日志

ERROR:

該級別的錯誤需要馬上被處理,當ERROR錯誤發生時,已經影響了用戶的正常訪問,是需要馬上得到管理員介入並處理的,常見的異常情況有:

  • 空指針異常
  • 打開配置文件失敗
  • 所有第三方對接的異常(包括第三方返回錯誤碼)
  • 所有影響功能使用的異常,包括:SQLException和除了業務異常之外的所有異常(RuntimeException和Exception)等等
    如果有Throwable信息,需要記錄完成的堆棧信息:
  log.error("獲取用戶[{}]的用戶信息時出錯", userName, e);

如果進行了拋出異常操作,請不要記錄error日志,由最終處理方進行處理:
反例(不要這么做)

  try {
           ....
        } catch (Exception e) {
		    String errorMessage = "收款單審核異常:";
            LOGGER.error("收款單審核異常", e);
            throw new ServiceException(errorMessage,e);
        }
ERROR:

不應該出現但是不影響程序、當前請求正常運行的異常情況:

  • 有容錯機制的時候出現的錯誤情況
  • 找不到配置文件,但是系統能自動創建配置文件
  • 性能即將接近臨界值的時候,例如:緩存池占用達到警告線
  • 業務異常的記錄,危險操作。比如:當接口拋出業務異常時,應該記錄此異常
INFO:

系統運行信息

  • Service方法中對於系統/業務狀態的變更
  • 主要邏輯中的分步驟
  • 定時任務等

外部接口部分

  • 客戶端請求參數
  • 遠程服務調用參數和調用結果

注意:

  1. 並不是所有的service都進行出入口打點記錄,單一、簡單service是沒有意義的
 public List<BankInfoRspDTO> queryBankInfoById(Integer id) {
	log.info("開始查詢銀行信息,id: [{}]" + id);
	List<BankInfoRspDTO> list = mapper.findPageByParam(pageParam);;
	log.info("查詢銀行信息結束");
	return list;
    }
  1. 對於復雜的業務邏輯,需要進行日志打點,以及埋點記錄,比如電商系統中的下訂單邏輯,以及業務狀態變更。

  2. 對於整個系統的提供出的接口,使用info記錄入參

  3. 調用其他第三方服務時,所有的出參和入參是必須要記錄的(因為你很難追溯第三方模塊發生的問題)

DEBUG

在生產環境中不能打印DEBUG級別的日志,DEBUG級別的日志只能用於開發調試或測試環節,同時在輸出DEBUG級別的日志的時候,也應該依據項目組的開發需求打印日志,盡量做到:

  • 開發人員和測試人員都能看懂
  • 通過閱讀DEBUG級別的日志后不需要重現問題,就能准確的定位解決問題

如果代碼中出現以下代碼,可以進行優化:

//1. 獲取用戶基本信息

//2. 獲取用戶休假情況

//3. 計算用戶應得薪資

優化后的代碼:

logger.debug("開始獲取員工[{}]基本信息",user);
logger.debug("獲取員工[{}]年的基本性別為[{}],年齡為[{}]", user, sex,age);

logger.debug("開始獲取員工[{}] [{}]年[{}]月休假情況",employee,year,month);
logger.debug("員工[{}][{}]年[{}]月年假/病假/事假為[{}]/[{}]/[{}]",employee,year,month,annualLeaveDays,sickLeaveDays,noPayLeaveDays);

logger.debug("開始計算員工[{}][{}]年[{}]月應得薪資",employee,year,month);
logger.debug("員工[{}] [{}]年[{}]月應得薪資為[{}]",employee,year,month,actualSalary);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM