1. 概述
日志记录是软件开发中的关键环节。在 Java 生态中,Log4J 凭借其灵活性和简洁性,数十年来一直是最流行的日志框架。
Log4j 2 是经典 Log4j 框架的全新升级版本。本文将通过实际示例,介绍最常用的 Appender、Layout 和 Filter。
在 Log4J2 中:
- Appender 是日志事件的输出目标,可以是简单的控制台,也可以是复杂的 RDBMS
- Layout 决定日志的呈现格式
- Filter 根据各种条件过滤日志数据
2. 环境准备
为了理解不同日志组件及其配置,我们将设置多个测试用例,每个用例包含一个 log4J2.xml
配置文件和一个 JUnit 4
测试类。
所有示例共需两个 Maven 依赖:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
除了主要的 log4j-core
包,我们还需要包含其测试 jar,以访问测试非常规配置文件所需的上下文规则。
3. 默认配置
ConsoleAppender
是 Log4J 2
核心包的默认配置。它按简单模式将日志输出到系统控制台:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="ConsoleAppender" target="SYSTEM_OUT">
<PatternLayout
pattern="%d [%t] %-5level %logger{36} - %msg%n%throwable"/>
</Console>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="ConsoleAppender"/>
</Root>
</Loggers>
</Configuration>
XML 配置关键标签解析:
Configuration
:Log4J2 配置文件的根元素,status
属性控制内部 Log4J 事件的日志级别Appenders
:包含一个或多个 Appender,此处配置输出到标准输出的控制台 AppenderLoggers
:可包含多个Logger
元素。特殊的Root
标签配置无名标准日志记录器,接收应用所有日志消息。每个日志记录器可设置最小日志级别AppenderRef
:定义对Appenders
部分元素的引用,ref
属性链接到 Appender 的name
属性
对应的单元测试同样简单:
@Test
public void givenLoggerWithDefaultConfig_whenLogToConsole_thanOK()
throws Exception {
Logger logger = LogManager.getLogger(getClass());
Exception e = new RuntimeException("This is only a test!");
logger.info("This is a simple message at INFO level. " +
"It will be hidden.");
logger.error("This is a simple message at ERROR level. " +
"This is the minimum visible level.", e);
}
4. 带自定义模式的 ConsoleAppender
我们在独立 XML 文件中定义带自定义颜色模式的控制台 Appender,并将其包含到主配置中:
<?xml version="1.0" encoding="UTF-8"?>
<Console name="ConsoleAppender" target="SYSTEM_OUT">
<PatternLayout pattern="%style{%date{DEFAULT}}{yellow}
%highlight{%-5level}{FATAL=bg_red, ERROR=red, WARN=yellow, INFO=green}
%message"/>
</Console>
此文件使用运行时由 Log4J2 替换的模式变量:
%style{...}{colorname}
:将括号内文本以指定颜色输出%highlight{...}{FATAL=colorname, ...}
:类似style
,但可为每个日志级别指定不同颜色%date{format}
:替换为指定格式的当前日期,此处使用默认格式yyyy-MM-dd HH:mm:ss,SSS
%-5level
:右对齐打印日志级别%message
:原始日志消息
主配置中引入该 Appender:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" xmlns:xi="http://www.w3.org/2001/XInclude">
<Appenders>
<xi:include href="log4j2-includes/
console-appender_pattern-layout_colored.xml"/>
</Appenders>
<Loggers>
<Root level="DEBUG">
<AppenderRef ref="ConsoleAppender"/>
</Root>
</Loggers>
</Configuration>
单元测试:
@Test
public void givenLoggerWithConsoleConfig_whenLogToConsoleInColors_thanOK()
throws Exception {
Logger logger = LogManager.getLogger("CONSOLE_PATTERN_APPENDER_MARKER");
logger.trace("This is a colored message at TRACE level.");
...
}
5. 异步文件 Appender:JSONLayout + BurstFilter
有时需要异步写入日志(例如应用性能比日志可用性更重要)。此时可用 AsyncAppender
。
示例配置异步 JSON 日志文件,并添加限制输出速率的突发过滤器:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
...
<File name="JSONLogfileAppender" fileName="target/logfile.json">
<JSONLayout compact="true" eventEol="true"/>
<BurstFilter level="INFO" rate="2" maxBurst="10"/>
</File>
<Async name="AsyncAppender" bufferSize="80">
<AppenderRef ref="JSONLogfileAppender"/>
</Async>
</Appenders>
<Loggers>
...
<Logger name="ASYNC_JSON_FILE_APPENDER" level="INFO"
additivity="false">
<AppenderRef ref="AsyncAppender" />
</Logger>
<Root level="INFO">
<AppenderRef ref="ConsoleAppender"/>
</Root>
</Loggers>
</Configuration>
关键配置说明:
JSONLayout
配置为每行一个日志事件BurstFilter
:当 INFO 及以上级别事件超过 2 个/秒时丢弃,最多丢弃 10 个AsyncAppender
缓冲区大小为 80 条日志,缓冲区满后刷新到文件
单元测试通过循环填充缓冲区,检查日志文件行数:
@Test
public void givenLoggerWithAsyncConfig_whenLogToJsonFile_thanOK()
throws Exception {
Logger logger = LogManager.getLogger("ASYNC_JSON_FILE_APPENDER");
final int count = 88;
for (int i = 0; i < count; i++) {
logger.info("This is async JSON message #{} at INFO level.", count);
}
long logEventsCount
= Files.lines(Paths.get("target/logfile.json")).count();
assertTrue(logEventsCount > 0 && logEventsCount <= count);
}
6. 滚动文件 Appender 与 XMLLayout
接下来创建滚动日志文件:文件达到指定大小时压缩并轮转。这次使用 XML 布局:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<RollingFile name="XMLRollingfileAppender"
fileName="target/logfile.xml"
filePattern="target/logfile-%d{yyyy-MM-dd}-%i.log.gz">
<XMLLayout/>
<Policies>
<SizeBasedTriggeringPolicy size="17 kB"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Logger name="XML_ROLLING_FILE_APPENDER"
level="INFO" additivity="false">
<AppenderRef ref="XMLRollingfileAppender" />
</Logger>
<Root level="TRACE">
<AppenderRef ref="ConsoleAppender"/>
</Root>
</Loggers>
</Configuration>
关键配置说明:
RollingFile
的filePattern
属性定义轮转文件名,支持占位符(示例包含日期和计数器)XMLLayout
默认配置写入无根元素的单个日志事件对象- 使用基于大小的策略轮转日志文件
单元测试类与上一节类似:
@Test
public void givenLoggerWithRollingFileConfig_whenLogToXMLFile_thanOK()
throws Exception {
Logger logger = LogManager.getLogger("XML_ROLLING_FILE_APPENDER");
final int count = 88;
for (int i = 0; i < count; i++) {
logger.info(
"This is rolling file XML message #{} at INFO level.", i);
}
}
7. Syslog Appender
当需要通过网络将日志事件发送到远程机器时,最简单的方式是使用 Log4J2 的 Syslog Appender
:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
...
<Syslog name="Syslog"
format="RFC5424" host="localhost" port="514"
protocol="TCP" facility="local3" connectTimeoutMillis="10000"
reconnectionDelayMillis="5000">
</Syslog>
</Appenders>
<Loggers>
...
<Logger name="FAIL_OVER_SYSLOG_APPENDER"
level="INFO"
additivity="false">
<AppenderRef ref="FailoverAppender" />
</Logger>
<Root level="TRACE">
<AppenderRef ref="Syslog" />
</Root>
</Loggers>
</Configuration>
Syslog
标签属性说明:
name
:定义 Appender 名称(同一应用/配置中需唯一)format
:设置为 BSD 或 RFC5424,决定 Syslog 记录格式host & port
:远程 Syslog 服务器的主机名和端口protocol
:使用 TCP 或 UDPfacility
:事件写入的 Syslog 设施connectTimeoutMillis
:等待建立连接的时间(默认 0)reconnectionDelayMillis
:重试连接前的等待时间
8. FailoverAppender
当主 Appender 处理日志事件失败时,为避免数据丢失,可使用 FailoverAppender
。
例如:当 Syslog
Appender 无法发送事件到远程机器时,临时回退到 FileAppender
:
<Failover name="FailoverAppender" primary="Syslog">
<Failovers>
<AppenderRef ref="ConsoleAppender" />
</Failovers>
</Failover>
测试代码:
@Test
public void givenLoggerWithFailoverConfig_whenLog_thanOK()
throws Exception {
Logger logger = LogManager.getLogger("FAIL_OVER_SYSLOG_APPENDER");
Exception e = new RuntimeException("This is only a test!");
logger.trace("This is a syslog message at TRACE level.");
logger.debug("This is a syslog message at DEBUG level.");
logger.info("This is a syslog message at INFO level.
This is the minimum visible level.");
logger.warn("This is a syslog message at WARN level.");
logger.error("This is a syslog message at ERROR level.", e);
logger.fatal("This is a syslog message at FATAL level.");
}
9. JDBC Appender
JDBC Appender 通过标准 JDBC 将日志事件发送到 RDBMS,连接可通过 JNDI 数据源或连接工厂获取。
基础配置包含 DataSource
/ConnectionFactory
、ColumnConfigs
和 tableName
:
<JDBC name="JDBCAppender" tableName="logs">
<ConnectionFactory
class="com.baeldung.logging.log4j2.tests.jdbc.ConnectionFactory"
method="getConnection" />
<Column name="when" isEventTimestamp="true" />
<Column name="logger" pattern="%logger" />
<Column name="level" pattern="%level" />
<Column name="message" pattern="%message" />
<Column name="throwable" pattern="%ex{full}" />
</JDBC>
测试代码:
@Test
public void givenLoggerWithJdbcConfig_whenLogToDataSource_thanOK()
throws Exception {
Logger logger = LogManager.getLogger("JDBC_APPENDER");
final int count = 88;
for (int i = 0; i < count; i++) {
logger.info("This is JDBC message #{} at INFO level.", count);
}
Connection connection = ConnectionFactory.getConnection();
ResultSet resultSet = connection.createStatement()
.executeQuery("SELECT COUNT(*) AS ROW_COUNT FROM logs");
int logCount = 0;
if (resultSet.next()) {
logCount = resultSet.getInt("ROW_COUNT");
}
assertTrue(logCount == count);
}
10. 总结
本文通过简单示例展示了如何在 Log4J2 中使用不同的日志 Appender、Filter 和 Layout,以及它们的配置方式。关键点总结:
✅ 核心组件关系:
- Appender 决定日志输出目标(控制台/文件/数据库等)
- Layout 控制日志格式(文本/JSON/XML等)
- Filter 实现日志过滤(级别/速率/内容等)
⚠️ 常见踩坑:
- 异步 Appender 需合理设置缓冲区大小
- 滚动文件需注意
filePattern
占位符匹配 - Failover 机制需测试主备切换场景
🚀 最佳实践:
- 生产环境推荐异步日志提升性能
- 关键业务日志建议多 Appender 冗余
- 复杂布局优先考虑结构化格式(JSON/XML)