monitorInterval 自動感知配置變更,無需重啟logger.debug(() -> expensiveComputation()) 避免無效字符串拼接
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.23.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.23.1</version>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.4</version>
</dependency>
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4j2Demo {
// 推薦:按類名獲取,支持層級繼承
private static final Logger logger = LogManager.getLogger(Log4j2Demo.class);
// 也可自定義名稱
// private static final Logger logger = LogManager.getLogger("my.custom.logger");
public void demo() {
// 入口/出口追蹤 (TRACE 級別)
logger.entry();
logger.entry("param1", "param2");
// 標準級別 (由低到高)
logger.trace("追蹤信息:方法進入,變量值={}", "value");
logger.debug("調試信息:當前用戶ID={}", 1001);
logger.info("業務信息:用戶[{}]登錄成功", "zhangsan");
logger.warn("警告信息:緩存命中率降至 {}%", 45);
logger.error("錯誤信息:數據庫連接失敗", new SQLException("Connection timeout"));
logger.fatal("致命錯誤:系統即將關閉");
// 顯式指定級別 (罕用)
logger.log(Level.DEBUG, "手動指定級別");
// 異常堆棧自動記錄
try {
int i = 1 / 0;
} catch (Exception e) {
logger.catching(e); // 等同於 logger.error("msg", e)
}
// 退出追蹤
logger.exit();
logger.exit("returnValue", result);
}
}
OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL
additivity="true",子 Logger 會向父 Logger (最終到 Root) 傳遞事件,導致重複輸出<Logger name="com.myapp" level="DEBUG" additivity="false"> 避免雙重輸出
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="60">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="60">
<!-- 全局變量定義 -->
<Properties>
<Property name="LOG_DIR">logs</Property>
<Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} %L %M - %msg%n</Property>
<Property name="FILE_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36} %L %M - %msg%n</Property>
</Properties>
<Appenders>
<!-- 1. 控制台:僅輸出 WARN 以上,開發調試用 -->
<Console name="Console" target="SYSTEM_OUT">
<ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
</Console>
<!-- 2. 普通日誌文件:每日滾動,保留 30 天,INFO 以上 -->
<RollingFile name="RollingFile"
fileName="${LOG_DIR}/app.log"
filePattern="${LOG_DIR}/$${date:yyyy-MM}/app-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="${FILE_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="100MB"/>
</Policies>
<DefaultRolloverStrategy max="20"/>
</RollingFile>
<!-- 3. 錯誤專用文件:單獨記錄 ERROR+,便於告警排查 -->
<RollingFile name="ErrorFile"
fileName="${LOG_DIR}/error.log"
filePattern="${LOG_DIR}/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log.gz">
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${FILE_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="50MB"/>
</Policies>
<DefaultRolloverStrategy max="30"/>
</RollingFile>
<!-- 4. 審計/業務專用:異步寫入,高吞吐 -->
<Async name="AsyncAudit">
<AppenderRef ref="AuditFile"/>
</Async>
<RollingFile name="AuditFile"
fileName="${LOG_DIR}/audit.log"
filePattern="${LOG_DIR}/audit-%d{yyyy-MM-dd}.log.gz">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %msg%n"/>
</RollingFile>
</Appenders>
<Loggers>
<!-- 包級別配置:com.myapp.dao 只輸出 WARN+,減少 SQL 干擾 -->
<Logger name="com.myapp.dao" level="WARN" additivity="false">
<AppenderRef ref="RollingFile"/>
<AppenderRef ref="ErrorFile"/>
</Logger>
<!-- 特定類 DEBUG -->
<Logger name="com.myapp.service.OrderService" level="DEBUG" additivity="false">
<AppenderRef ref="RollingFile"/>
<AppenderRef ref="ErrorFile"/>
</Logger>
<!-- 審計日誌獨立路由 -->
<Logger name="AUDIT" level="INFO" additivity="false">
<AppenderRef ref="AsyncAudit"/>
</Logger>
<Root level="INFO">
<AppenderRef ref="RollingFile"/>
<AppenderRef ref="ErrorFile"/>
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
| 佔位符 | 含義 | 示例 |
|---|---|---|
| %d{pattern} | 時間 | %d{yyyy-MM-dd HH:mm:ss.SSS} |
| %t | 線程名 | main, http-nio-8080-exec-1 |
| %-5level | 級別左對齊 5 寬 | INFO , DEBUG, ERROR |
| %logger{36} | Logger 名稱,精簡到 36 字符 | c.m.s.OrderService |
| %L | 行號 (慎用,有性能開銷) | 123 |
| %M | 方法名 (慎用) | processOrder |
| %msg / %m | 日誌消息 | 用戶登錄成功 |
| %ex / %xEx | 異常堆棧 (含/不含包名) | java.lang.NullPointerException... |
| %X{key} | MDC 值 (如 traceId) | %X{traceId} |
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="100MB"/>
<CronTriggeringPolicy schedule="0 0 * * * ?"/>
</Policies>
// 請求進入時設置 (如 Filter/Interceptor)
MDC.put("traceId", UUID.randomUUID().toString());
MDC.put("userId", currentUser.getId());
try {
logger.info("處理訂單請求"); // 自動包含 traceId, userId
// 業務邏輯...
} finally {
MDC.clear(); // 必須清理,防止線程池復用污染
}
// 配置中使用 %X{traceId} 輸出
<AsyncLogger name="com.myapp.highthroughput" level="INFO" additivity="false">
<AppenderRef ref="RollingFile"/>
</AsyncLogger>
<Async name="AsyncFile">
<AppenderRef ref="RollingFile"/>
</Async>
性能提示:異步日誌將日誌事件放入無鎖環形緩衝區,由專用線程批量寫入磁盤,主業務線程幾乎零延遲。需引入 Disruptor 依賴。
<CustomLevels>
<CustomLevel name="AUDIT" intLevel="350" />
</CustomLevels>
logger.log(Level.getLevel("AUDIT"), "審計記錄");
// Lookup 動態值
<Property name="LOG_DIR">${sys:user.home}/logs</Property>
<Property name="LOG_DIR">${env:LOG_PATH}</Property>
<Property name="LOG_DIR">${k8s:podName}</Property>
ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
builder.setStatusLevel(Level.WARN)
.add(builder.newAppender("Console", "CONSOLE")
.addAttribute("target", "SYSTEM_OUT")
.add(builder.newLayout("PatternLayout")
.addAttribute("pattern", "%d{HH:mm:ss} %-5level %logger - %msg%n")))
.add(builder.newRootLogger(Level.INFO)
.add(builder.newAppenderRef("Console")));
Configurator.initialize(builder.build());
log4j2.xml 是否在 classpath 根目錄 (src/main/resources)status="DEBUG" 觀察 Log4j2 內部啟動日誌log4j2.xml, log4j2-test.xml, log4j2.properties)additivity="false" 在具體 Logger 上Runtime.getRuntime().addShutdownHook(new Thread(LogManager::shutdown))log4j-web 模塊自動清理LogManager.shutdown() 在 ServletContextListener.contextDestroyedlog4j-1.2-api 橋接舊代碼 Logger.getLogger()log4j-slf4j2-impl 替換 log4j-slf4j-implLog4j2 以卓越性能、靈活配置、豐富生態成為 Java 日誌首選。掌握「分級路由、異步寫入、MDC 追蹤、熱加載」四大核心能力,配合合理的 Appender 組合(控制台+滾動文件+錯誤專用+審計異步),可構建生產級可觀測體系。建議在項目中統一接入 SLF4J 門面,底層無縫切換 Log4j2,享受最佳開發體驗。