Apache Log4j 是 Java 生態最經典的日誌框架,雖然 Log4j 2.x 與 SLF4J/Logback 已成主流,但大量存量系統仍運行在 Log4j 1.x 上。本文系統梳理 Log4j 1.x 核心配置、三大組件架構、輸出格式化、多環境部署最佳實踐。
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version> <!-- 1.2.15+ 修復安全漏洞 -->
</dependency>
log4j.properties (放在 classpath 根目錄 / src/main/resources)# 根 Logger:級別 INFO,輸出到 RollingFileAppender (RF) 與 Console (stdout)
log4j.rootLogger=INFO, RF, stdout
# 滾動文件輸出:單文件 100MB,保留 10 個備份
log4j.appender.RF=org.apache.log4j.RollingFileAppender
log4j.appender.RF.File=logs/app.log
log4j.appender.RF.MaxFileSize=100MB
log4j.appender.RF.MaxBackupIndex=10
log4j.appender.RF.layout=org.apache.log4j.PatternLayout
log4j.appender.RF.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} [%p] [%t] [%c{1}:%L] - %m%n
# 控制台輸出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} [%p] [%t] [%c{1}:%L] - %m%n
# 包級別覆蓋:com.demo.dao 開啟 DEBUG
log4j.logger.com.demo.dao=DEBUG, RF
log4j.additivity.com.demo.dao=false # 禁用向根 Logger 遞交,避免重複輸出
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
public class LogDemo {
private static final Logger LOG = Logger.getLogger(LogDemo.class);
public static void main(String[] args) {
// 方式 1:自動加載 classpath 下 log4j.properties (推薦)
// 方式 2:手動指定路徑
// PropertyConfigurator.configure("log4j.properties");
// PropertyConfigurator.configure(Thread.currentThread().getContextClassLoader().getResource("log4j.properties").getFile());
LOG.debug("調試信息 - 僅 com.demo.dao 可見");
LOG.info("業務信息 - 正常流程記錄");
LOG.warn("警告信息 - 潛在風險");
LOG.error("錯誤信息 - 異常堆棧", new RuntimeException("測試異常"));
}
}
Logger (記錄器):日誌對象入口,層級體系 root → com → com.demo → com.demo.dao,支援繼承與覆蓋。
Appender (輸出目的地):決定日誌寫往何處。
Layout (佈局格式化):決定日誌長什麼樣。
| Appender | 用途 | 關鍵屬性 |
|---|---|---|
| ConsoleAppender | 控制台輸出 | target=System.out/err |
| FileAppender | 固定文件 | File, Append |
| RollingFileAppender | 按大小滾動 | MaxFileSize, MaxBackupIndex |
| DailyRollingFileAppender | 按日期滾動 | DatePattern (yyyy-MM-dd) |
| JDBCAppender | 寫入數據庫 | URL, SQL, 驅動配置 |
| SocketAppender | 網絡傳輸 | RemoteHost, Port |
| AsyncAppender | 異步緩衝 (高性能) | BufferSize, Blocking |
| 佔位符 | 含義 | 示例輸出 |
|---|---|---|
| %d{pattern} | 時間戳 | 2024-01-15 14:30:45.123 |
| %p | 級別 | DEBUG/INFO/WARN/ERROR |
| %c{length} | Logger 名稱 (length 縮寫層級) | c.d.dao.UserDao |
| %t | 線程名 | http-nio-8080-exec-3 |
| %L | 行號 | 42 |
| %F | 文件名 | UserDao.java |
| %m | 日誌消息 | 用戶登入成功 |
| %n | 換行符 (跨平台) | \r\n / \n |
| %x | NDC (嵌套診斷上下文) | userId=1001 |
| %% | 字面 % 字符 | % |
# 標準單行格式 (便於 ELK/Graylog 解析)
%d{yyyy-MM-dd HH:mm:ss.SSS} [%p] [%t] [%c{1}:%L] - %m%n
# JSON 格式 (需自定義 Layout 或使用 Log4j 2.x)
{"timestamp":"%d{ISO8601}","level":"%p","thread":"%t","logger":"%c","line":%L,"msg":"%m"}%n
# log4j-dev.properties (開發環境:控制台 DEBUG)
log4j.rootLogger=DEBUG, stdout
# log4j-prod.properties (生產環境:文件 INFO + 異步)
log4j.rootLogger=INFO, async
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=/var/log/app/app.log
log4j.appender.file.MaxFileSize=500MB
log4j.appender.file.MaxBackupIndex=30
log4j.appender.async=org.apache.log4j.AsyncAppender
log4j.appender.async.BufferSize=1000
log4j.appender.async.Blocking=true
log4j.appender.async.appenders=file
啟動參數切換:-Dlog4j.configuration=file:/opt/config/log4j-prod.properties
log4j.appender.async=org.apache.log4j.AsyncAppender
log4j.appender.async.BufferSize=8192
log4j.appender.async.Blocking=false # 隊列滿丟棄不阻塞業務線程
log4j.appender.async.appenders=RF
原理:業務線程僅將日誌事件放入環形緩衝區,專門的分發線程批量寫入磁盤,將 I/O 延遲從關鍵路徑剝離。
// 請求進入時 (Filter/Interceptor)
MDC.put("traceId", UUID.randomUUID().toString().replace("-", ""));
MDC.put("userId", String.valueOf(currentUser.getId()));
// 日誌自動攜帶 %X{traceId} %X{userId}
log.info("處理訂單");
// 輸出: 2024-01-15 14:30:45 [INFO] [http-nio-8080-exec-3] [OrderService:52] - traceId=a1b2c3 userId=1001 處理訂單
// 請求結束清理 (防止線程池復用污染)
MDC.clear();
-Dlog4j.configuration=file:/path/log4j.propertieslog4j.properties / log4j.xmlPropertyConfigurator.configure(...)| 問題 | 原因 | 解決 |
|---|---|---|
| 日誌不輸出/級別不生效 | 配置文件未加載、Logger 名稱拼寫錯誤、additivity 導致重複/遺漏 | 檢查 classpath、啟用 -Dlog4j.debug 診斷、確認包名層級 |
| 文件不滾動/備份丟失 | MaxFileSize 單位錯誤 (KB/MB/GB)、權限不足、磁盤滿 | 單位大寫、檢查進程寫權限、監控磁盤空間 |
| 中文亂碼 | 文件編碼與 JVM file.encoding 不一致 | 配置 log4j.appender.RF.Encoding=UTF-8、JVM 參數 -Dfile.encoding=UTF-8 |
| 內存洩漏 (AsyncAppender) | BufferSize 過大、Blocking=false 導致無限堆積 | 設置合理 BufferSize (1000-10000)、關鍵日誌用同步 Appender |
| Web 應用卸載報錯 | Log4j 線程未正常關閉 | 配置 Log4jConfigListener 或 ServletContextListener 手動 LogManager.shutdown() |
log4j-1.2-api 橋接包 → 替換配置文件語法 → 逐步替換代碼調用Log4j 通過 Logger 分級控制 + Appender 多端輸出 + Layout 靈活格式化 三大組件解耦,構建了強大的日誌體系。生產環境務必掌握:異步輸出提升性能、MDC 實現鏈路追蹤、滾動策略防止磁盤爆滿、多環境配置隔離。新項目強烈建議直接採用 Log4j 2.x + SLF4J 或 Logback + SLF4J,享受現代日誌框架的性能與功能紅利。