單執行緒應用的追蹤較簡單,但多執行緒、分散式系統中,日誌交織、時間戳不同步、資訊量爆炸,導致「有日誌無從下手」。本文構建「豐富日誌產出 + UNIX 指令挖掘」雙引擎除錯體系,核心工具:**Log4j**(結構化日誌)+ **UNIX 文字處理指令**(高效查詢分析)。
log4j.rootLogger=debug, stdout, R
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %5p [%t] (%F:%L) - %m%n
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=example.log
log4j.appender.R.MaxFileSize=1000KB
log4j.appender.R.MaxBackupIndex=1
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d %5p [%t] (%F:%L) - %m%n
輸出範例:
2004-01-24 15:00:23,714 INFO [Thread-1] (SampleCode.java:67) - [-992285010] Program Running
關鍵欄位解析:
%d:ISO8601 時間戳(含毫秒),支援跨機器排序%5p:等級(DEBUG/INFO/WARN/ERROR/FATAL),固定寬度便於對齊[%t]:JVM 執行緒名稱(如 Thread-1、ORB.thread.pool:0)(%F:%L):來源檔案名與行號,精準定位代碼[-992285010]:業務追蹤 ID(隨機數),貫穿多執行緒、多 JVM、多機器的完整呼叫鏈Random.nextInt() 作為 traceId[traceId]JVM執行緒名 + traceId 實現全域唯一性Client (50 threads) → log1Bean (Server 1) → log2Bean (Server 2)
// Client 執行緒
int traceId = new Random().nextInt();
logger.info("[" + traceId + "] Program Running");
logger.info("[" + traceId + "] Calling Create on log1Home");
// log1Bean
public boolean doTest(int traceId) {
logger.info("[" + traceId + "] Started doTest in log1Bean");
int choice = new Random().nextInt(10);
logger.debug("[" + traceId + "] Random choice: " + choice);
if (choice <= 5) {
log2Bean lb2 = home.create();
lb2.doTest(traceId); // 透傳 traceId
}
}
// log2Bean
public boolean doTest(int traceId) {
logger.info("[" + traceId + "] Started doTest in log2Bean");
logger.info("[" + traceId + "] Path ends in Log2Bean");
}
| 指令 | 用途 |
|---|---|
| 按時間戳全域排序,重建時序 | |
| 依 JVM 執行緒名過濾 | |
| 終極武器:重建單一業務完整呼叫鏈 | |
| 系統快照:某秒發生的所有事件 | |
| 提取所有 traceId | |
| 自動拆分:每個 traceId 產生一個獨立日誌檔 |
# 1. 時段前有日誌的 traceId
awk '{ if ($2 < "18:18:50") print }' *.log | cut -d'[' -f3 | cut -d']' -f1 | uniq > before.txt
# 2. 時段後有日誌的 traceId
awk '{ if ($2 > "18:18:51") print }' *.log | cut -d'[' -f3 | cut -d']' -f1 | uniq > after.txt
# 3. 交集 = 該時段存活的執行緒
cat before.txt | awk '{system("grep " $1 " after.txt")}' | uniq | \
awk '{system("grep " $1 " *.log > log"$1".txt")}'
| 等級 | 用途 | 範例 |
|---|---|---|
| DEBUG | 變數值、流程細節 | Random choice: 5 |
| INFO | 方法進出、關鍵節點、成功狀態 | Started doTest, Execution ran successfully |
| WARN | 可重試失敗、降級處理 | Connection retry 1/3 |
| ERROR | 業務錯誤、邏輯異常 | Failed to create remote EJB |
| FATAL | 系統級不可恢復錯誤 | OutOfMemoryError, 配置檔遺失 |
鐵律:專案啟動前制定《日誌規範文件》,全團隊遵守,Code Review 強制檢查。
AsyncAppender 降低日誌對業務執行緒影響log4j.logger.com.myapp.dao=DEBUG 精細控制「好記性不如爛筆頭,好腦袋不如好工具。」Log4j 提供結構化、可配置、高效能的日誌產出;UNIX 指令提供零成本、極高彈性、管線化的分析能力。兩者結合,將多執行緒、多平台的除錯難題轉化為可系統化、可自動化的工程流程。投入少量精力建立日誌規範與 Trace ID 機制,換來的是事半功倍的診斷效率——這是每個嚴肅工程團隊必備的核心能力。