Apache Log4j 2 是 Java 生態新一代高性能日誌框架,相比 Log4j 1.x (已停止維護) 與 Logback,提供 **無鎖異步日誌 (LMAX Disruptor)、插件架構、Lambda 延遲求值、自動重載配置、Garbage-Free 佈局** 等核心優勢。本文系統講解 Log4j 2 在 Web 項目中的配置全流程:依賴引入、配置文件詳解、Web 容器集成、多環境隔離、異步鑒別器、性能調優與避坑指南。
<dependencies>
<!-- API 接口 -->
<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>
<!-- Web 容器集成 (自動初始化/關閉、MDC 繼承) -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>2.23.1</version>
</dependency>
<!-- SLF4J 橋接 (若項目用 SLF4J API) -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.23.1</version>
</dependency>
<!-- 異步日誌依賴 (LMAX Disruptor) -->
<dependency>
<groupId>com.lmax</groupId>
disruptor
3.4.4
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="30"> <!-- monitorInterval: 30秒自動重載 -->
<!-- 1. 全局變量定義 -->
<Properties>
<Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Property>
<Property name="LOG_DIR">${sys:catalina.base}/logs</Property> <!-- Tomcat 基礎目錄 -->
<Property name="APP_NAME">my-webapp</Property>
</Properties>
<!-- 2. Appenders: 日誌輸出目的地 -->
<Appenders>
<!-- 控制台輸出 (開發環境) -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${LOG_PATTERN}"/>
</Console>
<!-- 滾動文件輸出 (生產核心) -->
<RollingFile name="RollingFile" fileName="${LOG_DIR}/${APP_NAME}.log"
filePattern="${LOG_DIR}/$${date:yyyy-MM}/${APP_NAME}-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/> <!-- 每天滾動 -->
<SizeBasedTriggeringPolicy size="100 MB"/> <!-- 或達到100MB滾動 -->
</Policies>
<DefaultRolloverStrategy max="30"/> <!-- 保留30個歸檔 -->
</RollingFile>
<!-- 錯誤日誌單獨文件 (便於告警掃描) -->
<RollingFile name="ErrorFile" fileName="${LOG_DIR}/${APP_NAME}-error.log"
filePattern="${LOG_DIR}/$${date:yyyy-MM}/${APP_NAME}-error-%d{yyyy-MM-dd}-%i.log.gz">
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/> <!-- 只記ERROR及以上 -->
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="50 MB"/>
</Policies>
<DefaultRolloverStrategy max="60"/>
</RollingFile>
<!-- 異步 Appender 包裝 (關鍵性能優化) -->
<Async name="AsyncRoot">
<AppenderRef ref="RollingFile"/>
<AppenderRef ref="ErrorFile"/>
<AppenderRef ref="Console"/>
<!-- 队列滿策略: 默認阻塞, 可配 discard/block -->
</Async>
</Appenders>
<!-- 3. Loggers: 定義日誌級別與輸出路由 -->
<Loggers>
<!-- 根 Logger -->
<Root level="INFO">
<AppenderRef ref="AsyncRoot"/> <!-- 統一走異步 -->
</Root>
<!-- 包級別精確控制 -->
<Logger name="com.myapp.dao" level="DEBUG" additivity="false"> <!-- additivity=false 不向父傳遞 -->
<AppenderRef ref="AsyncRoot"/>
</Logger>
<Logger name="org.springframework" level="WARN"/>
<Logger name="org.hibernate.SQL" level="DEBUG"/> <!-- 打印SQL -->
<Logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/> <!-- 打印參數 -->
</Loggers>
</Configuration>
log4j-web 模塊利用 `ServletContainerInitializer` 自動完成:
無需在 web.xml 配置任何內容,只需引入 `log4j-web` 依賴。若需自定義配置文件位置:
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>classpath:log4j2-prod.xml</param-value>
</context-param>
Spring Boot 3 默認使用 Logback,**需排除後引入 Log4j 2**:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId> <!-- 排除 Logback -->
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
配置文件命名為 `log4j2-spring.xml` 可使用 Spring Profile (`
logging:
level:
root: INFO
com.myapp: DEBUG
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
logback:
rollingpolicy:
max-file-size: 100MB
max-history: 30
-DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
// 所有 Logger 自動異步,無需 包裝,吞吐量提升 6-68 倍
或配置文件中指定 `
<Logger name="com.myapp.audit" level="INFO" additivity="false">
<AppenderRef ref="AuditFile"/>
<ScriptFilter><![CDATA[
// 只記錄包含 auditCode=PAY_SUCCESS 的日誌
import org.apache.logging.log4j.Marker;
if (marker != null && "PAY_SUCCESS".equals(marker.getName())) {
return true;
}
return false;
]]></ScriptFilter>
</Logger>
<!-- Filter 配置在 web.xml 或 Spring Boot FilterRegistrationBean -->
public class RequestIdFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
String requestId = UUID.randomUUID().toString().replace("-", "");
MDC.put("requestId", requestId); // 放入線程局部Map
try { chain.doFilter(req, res); }
finally { MDC.remove("requestId"); }
}
}
// log4j2.xml PatternLayout 引用: %X{requestId}
<PatternLayout pattern="%d %X{requestId} [%t] %-5level %logger - %msg%n"/>
<!-- 需 log4j-layout-template-json 依賴 -->
<JsonTemplateLayout eventTemplateUri="classpath:EcsLayout.json"/>
// EcsLayout.json 定義 Elastic Common Schema 結構
| 優化點 | 配置/參數 | 收益 |
|---|---|---|
| 啟用異步日誌 | -DLog4jContextSelector=AsyncLoggerContextSelector | 吞吐量提升 10x+, 延遲降低 90%+ |
| 使用 Direct Buffer | -Dlog4j2.enable.direct.encoders=true | 減少堆外拷貝 GC 壓力 |
| 關閉位置信息 | PatternLayout 不使用 %C, %M, %L, %class, %method, %line, %location | 避免填充堆棧幀,性能提升 30%+ |
| Lambda 延遲求值 | logger.debug(() -> "Expensive: " + compute()) | DEBUG 關閉時零開銷 |
| 合理設置緩衝區 | RollingFile | 減少系統調用 |
| 避免同步文件刷新 | immediateFlush="false" (異步模式默認關閉) | 批量落盤 |
Log4j 2 憑藉 **異步架構、插件化、零 GC 布局** 成為現代 Java Web 項目日誌首選。核心落地要點:**引入 log4j-web 實現零配置集成、採用 XML 配置 RollingFile+AsyncAppender、啟用全局異步上下文選擇器、嚴禁位置信息與生產 DEBUG、善用 MDC 串聯鏈路**。遵循本指南配置,可構建高性能、可維護、可觀測的生產級日誌體系。