log.debug("Found {} records for filter: '{}'", count, filter) —— 避免字符串拼接開銷,延遲 toString() 調用StopWatch 替代 System.currentTimeMillis(),生成圖表分析| 級別 | 使用場景 | 示例 |
|---|---|---|
| ERROR | 系統不可用、數據損失、需立即介入 | NPE、DB 連接失敗、關鍵業務拋異常 |
| WARN | 潛在問題、降級策略觸發、配置不當 | 緩存未命中回源、配置文件缺失用默認值 |
| INFO | 關鍵業務節點、狀態變更、審計追蹤 | 用戶登錄、訂單創建、支付回調、定時任務啟停 |
| DEBUG | 開發調試、參數傳遞、中間狀態 | 方法入參/出參、SQL 語句、緩存命中詳情 |
| TRACE | 極細粒度、臨時診斷、框架內部流程 | 循環迭代變量、框架攔截器鏈路 |
原則:生產環境默認 INFO,排查問題臨時調 DEBUG;代碼中不要寫 if (log.isDebugEnabled()),SLF4J 佔位符已內建延遲求值。
"Processing order {}: status {} -> {}" 而非 "Processing"
// ❌ 魔法字符串
log.info("&&&!#12345");
// ❌ 空日誌依賴行號
log.info("");
// ❌ 吞異常
try { log.trace("..."); } catch (NPE e) {}
// ✅ 正確
log.debug("Enter processOrder(orderId={}, userId={})", orderId, userId);
log.debug("Leave processOrder(): orderId={}, duration={}ms", orderId, duration);
try {
// 業務代碼
} catch (IOException e) {
// ✅ 正確:業務描述 + 異常對象(自動打印堆棧)
log.error("Failed to export report for order {}", orderId, e);
throw new BusinessException("導出失敗", e);
}
// ❌ 錯誤:重複打印堆棧
log.error("IO error", e);
throw new CustomException(e);
// ❌ 錯誤:丟失堆棧
log.error(e.getMessage());
log.error("{}", e);
log.error(e.toString());
SLF4J 只接受 log.error(String msg, Throwable t) 兩參數重載,第一參數為訊息模板,第二為異常對象。
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %-5level [%thread] [%logger{15}] %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/app.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
</root>
<!-- 特定包級別覆蓋 -->
<logger name="com.mycompany.dao" level="DEBUG"/>
<logger name="org.springframework.web" level="WARN"/>
</configuration>
%d{pattern}:時間,建議毫秒級 HH:mm:ss.SSS%-5level:級別左對齊 5 字符[%thread]:線程名,多線程排查必備%logger{length}:類名縮寫,{0} 只顯示簡類名,{15} 保留 15 字符%msg / %message:日誌內容%file、%line、%method —— 性能開銷大,現代 IDE 已支持點擊跳轉
// 請求進入時(Filter/Interceptor)
MDC.put("traceId", UUID.randomUUID().toString());
MDC.put("userId", currentUser.getId());
try {
// 業務處理,所有日誌自動攜帶 traceId/userId
log.info("Processing payment");
} finally {
MDC.clear(); // 必須清理,防止線程池復用洩露
}
配置模式加入 %X{traceId} 即可在日誌中顯示,配合 ELK/Graylog 實現全鏈路檢索。
<appender class="AsyncAppender"> 解耦 I/O 阻塞toString()優秀的日誌體系 = 統一門面 (SLF4J) + 高性能實現 (Logback) + 規範級別/內容/格式 + 結構化字段 (MDC) + 異步非阻塞輸出。日誌不僅是調試工具,更是系統可觀測性的基石。投資日誌規範,回報是故障排查效率的數量級提升。