java中日志打印規范


1.選擇恰當的日志級別

日常開發中常見日志級別有:trace、debug、info、warn、error(級別依次增大):
1.trace:最詳細的日志信息,一般記錄到日志文件中
2.debug:一般用於開發中DEBUG的關鍵邏輯的運行時數據
3.info:記錄排查問題的關鍵信息,如出參,入參等
4.warn:警告日志,一般的錯誤,對正常業務影響不大,需要開發者關注
5.error:錯誤日志,對正常業務有影響,需要運維配置日志監控

2.日志要打印方法的入參和出參

例如在Controller層,請求入參、響應出參和響應異常,一般需要打印日志,出問題時,方便追蹤代碼邏輯運行的路線。建議這里使用日志切面進行統一日志打印;

其他層級的方法入參和出參,如有必要,可以打印整個出參和入參的數據,反之可以打印有效的關鍵日志,方便問題定位即可;

3.日志格式

一般日志中包含:時間、日志級別、線程名稱、日志具體內容等

%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger:%line - %msg%n

4.在多個if-else等條件時,每個分支首行盡量打印日志

可以在進入分支前打印日志,后續可以快速定位到進入了哪個分支,方便排查問題

String requestNo = "RN7195458555001";
String channel = "weixin";
log.info("請求流水號[{}]支付處理,渠道為:{}", requestNo, channel);
if (Objects.equals("zhifubao", channel)) {
    // TODO
} else if (Objects.equals("yinlian", channel)) {
    // TODO
} else if (Objects.equals("weixin", channel)) {
    // TODO
} else {
    // TODO
}

5.日志級別比較低時,進行日志開關判斷

對於trace、debug級別的日志打印,需要進行開關判斷

if (log.isTraceEnabled()) {
    log.trace("trace log detail......");
}
if (log.isDebugEnabled()) {
    log.debug("debug log detail......");            
}

比如需要打印如下日志

log.debug("Payment processing,requestNo=" + requestNo + ",channel=" + channel);

當前配置的日志級別為info,則上面日志不會打印,但會進行字符串拼接;如果是對象,還會執行toString()方法,浪費了系統資源

6.不要直接使用日志系統(Log4j、Logback)中的 API,而是使用日志框架SLF4J中的API

SLF4J是門面模式的日志框架,需要更換日志框架實現時,在不改動代碼的情況下,可以方便切換到不同的日志實現框架

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory;

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

// 或者使用Lombok @Slf4j

7.建議使用參數占位{},而不是用+拼接

反例:

log.debug("Payment processing,requestNo=" + requestNo + ",channel=" + channel);

字符串使用"+"進行拼接操作,會有一定的性能損耗,雖然高版本的jdk對字符串拼接進行了性能優化,但不建議使用

正例:

log.info("Payment processing,requestNo={},channel={}", requestNo, channel);

使用大括號{}進行占位符的替換,相比字符串拼接,性能上更高,日志代碼也更加優雅

8.不要使用e.printStackTrace() 和 System.out.println();

反例:

try{
  // TODO 業務代碼處理
}catch(Exception e){
 //System.out.println("xxx業務處理異常,異常信息:" + e); e.printStackTrace(); }

正例:

try {
    // TODO 業務代碼處理
} catch (Exception e) {
    log.error("xxx業務處理異常 requestNo={}", requestNo, e);
}
e.printStackTrace()打印在堆棧信息中,如果異常過多,會導致堆棧內存不足,出現卡(運行極慢)的現象,最后出現OOM,這是一種非常糟糕的現象;
使用log來打印日志會記錄到日志文件中,占用的是磁盤內存,一般不會經常出現卡(運行極慢)的現象,但如果磁盤內存占用比較高時,需要對日志進行備份處理,然后清理日志;

9.異常日志不要只打一半,要輸出全部錯誤信息

反例:

log.error("xxx業務處理異常");
log.error("xxx業務處理異常", e.getMessage());

e.getMessage()不會記錄詳細的堆棧異常信息,只會記錄錯誤基本描述信息,不利於排查問題。

正例:

log.error("xxx業務處理異常 requestNo={}", requestNo, e);

10.禁止在線上環境開啟 debug

一般業務系統的debug日志較多,引入的第三方框架debug日志也較多,隨着業務交易的增多,容易占用磁盤內存空間,最后可能會影響正常業務系統的運行

,所以生產環境禁止開啟debug

11.不要既打印了異常日志,又拋出異常

try {
    // TODO 業務代碼處理
} catch (Exception e) {
    log.error("xxx業務處理異常", e);
    // BusinessException:自定義業務處理異常類
    throw new BusinessException("xxx業務處理異常", e);
}

此處會打印兩次異常日志:

第一次是log.error("xxx業務處理異常", e)會打印一次;
第二次是throw new BusinessException("xxx業務處理異常", e)會打印一次;

可以根據具體情況來選擇,例如此處就應該拋異常處理,那么可以僅使用throw new BusinessException("xxx業務處理異常", e)即可

12.避免重復打印日志

如果一行日志可以表達清楚,則使用一行打印即可,避免日志信息冗余

反例:

log.info("創建用戶信息 userId={}", userId);
log.info("創建用戶信息 userName={}", userName);

正例:

log.info("創建用戶信息 userId={},userName={}", userId, userName);

13.日志文件分離

根據不同的日志級別,打印在不同的日志文件中,例如debug、info、warn、error日志級別的日志分別創建一個日志文件debug.log、info.log、warn.log、error.log進行日志打印;

將不同類型的日志進行日志分類,例如access.log、error.log等

14.核心功能模塊,建議打印較完整的日志

業務系統中,核心功能的代碼,盡可能打印日志完整,核心代碼執行頻率極高,出問題時,根據日志信息能快速定位。

 


免責聲明!

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



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