1. 简介
本文将介绍如何为 Log4j2 创建一个自定义的 Appender。如果你还不熟悉 Log4j2 的基本概念,可以先阅读我们之前的这篇文章。
✅ Log4j2 内置了大量 Appender,支持日志输出到文件、数据库、Socket 甚至 NoSQL 存储,覆盖绝大多数常见场景。
但实际开发中,业务需求千奇百怪,比如你想把日志推送到消息队列、写入 Redis,或者做轻量级内存缓存用于调试——这时候就得自己动手写一个自定义 Appender 了。
⚠️ 注意:Log4j2 是 Log4j 的升级版,性能和扩展性都有显著提升。本文基于 Log4j2 演示,别再用老掉牙的 Log4j 了。
2. Maven 依赖配置
要使用 Log4j2 的核心功能,首先在 pom.xml
中引入 log4j-core
:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
📌 最新版本可前往 Maven Central 查看。
3. 自定义 Appender 实现
实现自定义 Appender 有两种方式:
- ✅ 实现
Appender
接口 - ✅ 继承
AbstractAppender
类(推荐)
我们推荐第二种,AbstractAppender
已经帮你处理了线程安全、过滤器、名称管理等通用逻辑,简单粗暴又省事。
示例:MapAppender
我们来实现一个 MapAppender
,它的功能是把日志事件(LogEvent)存进一个 ConcurrentHashMap
,以时间戳为 key,便于后续程序读取或调试。
@Plugin(
name = "MapAppender",
category = Core.CATEGORY_NAME,
elementType = Appender.ELEMENT_TYPE)
public class MapAppender extends AbstractAppender {
private ConcurrentMap<String, LogEvent> eventMap = new ConcurrentHashMap<>();
protected MapAppender(String name, Filter filter) {
super(name, filter, null);
}
@PluginFactory
public static MapAppender createAppender(
@PluginAttribute("name") String name,
@PluginElement("Filter") Filter filter) {
return new MapAppender(name, filter);
}
@Override
public void append(LogEvent event) {
eventMap.put(Instant.now().toString(), event);
}
}
关键点解析:
- ✅
@Plugin
注解:告诉 Log4j2 这是个插件,必须加上。name
:配置文件中使用的 Appender 名称category
:固定为Core.CATEGORY_NAME
elementType
:设为Appender.ELEMENT_TYPE
- ✅
@PluginFactory
:工厂方法,用于创建 Appender 实例。Log4j2 在解析配置时会调用它。 - ✅ 构造函数:调用父类构造器,传入 name 和 filter,layout 设为
null
表示使用默认布局。 - ✅
append(LogEvent)
:核心逻辑,每次日志输出都会调用这个方法。
⚠️ 踩坑提醒:@PluginFactory
方法必须是 public static
,参数用 @PluginAttribute
和 @PluginElement
标注,否则 Log4j2 找不到你的 Appender!
4. 配置文件使用
有了自定义 Appender,接下来在 log4j2.xml
中配置它。
1. 启用插件扫描
<Configuration xmlns:xi="http://www.w3.org/2001/XInclude"
packages="com.baeldung"
status="WARN">
📌 packages="com.baeldung"
是关键!Log4j2 会扫描这个包下的所有 @Plugin
类。确保你的 MapAppender
在这个包路径下,否则插件不会被加载。
2. 定义 Appender
<MapAppender name="MapAppender" />
名字要和 @Plugin(name = "MapAppender")
保持一致。
3. 在 Logger 中引用
<Root level="DEBUG">
<AppenderRef ref="MapAppender" />
</Root>
这样所有日志都会经过 MapAppender
,并被存入内存 Map。
5. 错误处理
自定义 Appender 也要考虑异常情况。AbstractAppender
提供了 error()
方法,可用于记录内部错误。
比如我们想限制只记录 WARN
及以上级别的日志,低于的直接丢弃并报错:
@Override
public void append(LogEvent event) {
if (event.getLevel().isLessSpecificThan(Level.WARN)) {
error("Unable to log less than WARN level.");
return;
}
eventMap.put(Instant.now().toString(), event);
}
✅ error()
方法会把错误信息输出到 Log4j2 内部日志(由 status
控制),方便排查问题。
6. 总结
本文演示了如何创建一个简单的自定义 Log4j2 Appender:
- ✅ 使用
@Plugin
和@PluginFactory
注解注册插件 - ✅ 继承
AbstractAppender
简化实现 - ✅ 在
log4j2.xml
中通过packages
扫描并使用 - ✅ 合理使用
error()
处理异常
虽然 Log4j2 提供了丰富的内置 Appender,但自定义能力让你可以灵活应对特殊场景,比如监控、调试、集成内部系统等。
📌 完整代码示例已上传至 GitHub:https://github.com/eugenp/tutorials/tree/master/logging-modules/log4j2