log4j2如何缓存指定数量日志消息用于告警邮件等场景?
Log4j2 日志缓存功能实现方案
内置方案:无需编码的配置实现
Log4j2 原生提供了CyclicBufferAppender组件,完全匹配你需要的「缓存指定数量最新日志」的需求,仅通过配置即可实现,不需要额外开发:
- 功能逻辑:采用环形缓冲区实现,超过配置的缓存数量后,最早的日志会被新日志覆盖
- 配置示例(xml格式):
<Configuration status="WARN"> <Appenders> <!-- bufferSize指定缓存的日志条数,此处设置为10即可匹配你的原有需求 --> <CyclicBuffer name="RecentLogBuffer" bufferSize="10"/> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="RecentLogBuffer"/> </Root> </Loggers> </Configuration>
- 代码中获取缓存日志的方式:
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.appender.CyclicBufferAppender; import org.apache.logging.log4j.core.LogEvent; // 上下文获取固定写法 LoggerContext context = (LoggerContext) LogManager.getContext(false); CyclicBufferAppender bufferAppender = context.getConfiguration().getAppender("RecentLogBuffer"); // 获取已缓存的最新10条日志数组 LogEvent[] cachedLogs = bufferAppender.toArray();
自定义编码实现方案
如果内置的CyclicBufferAppender满足不了你的定制化逻辑,需要扩展功能,你可以对接Log4j2以下核心模块自定义实现缓存逻辑:
- 核心对接模块:
- 继承
org.apache.logging.log4j.core.appender.AbstractAppender抽象类,这是所有自定义Appender的基类 - 使用
@Plugin注解标记自定义类,Log4j2会自动扫描插件完成注册 - 重写
append()方法实现缓存逻辑,使用环形队列存储指定数量的日志事件即可
- 继承
- 最简自定义实现示例:
import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.AbstractAppender; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.config.plugins.PluginAttribute; import org.apache.logging.log4j.core.config.plugins.PluginFactory; import org.apache.logging.log4j.core.layout.PatternLayout; import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; @Plugin(name = "CustomCacheAppender", category = "Core", elementType = "appender") public class CustomCacheAppender extends AbstractAppender { private static final int DEFAULT_BUFFER_SIZE = 10; private final Queue<LogEvent> logBuffer; protected CustomCacheAppender(String name, Queue<LogEvent> logBuffer) { super(name, null, PatternLayout.createDefaultLayout(), true); this.logBuffer = logBuffer; } @Override public void append(LogEvent event) { // 日志事件转不可变实例,避免异步场景下内容被回收 LogEvent immutableEvent = event.toImmutable(); if (logBuffer.size() >= DEFAULT_BUFFER_SIZE) { logBuffer.poll(); } logBuffer.offer(immutableEvent); } // 对外暴露获取缓存日志的方法 public Queue<LogEvent> getCachedLogs() { return logBuffer; } @PluginFactory public static CustomCacheAppender createAppender( @PluginAttribute("name") String name, @PluginAttribute(value = "bufferSize", defaultInt = 10) int bufferSize ) { if (name == null) { LOGGER.error("CustomCacheAppender must have a name"); return null; } Queue<LogEvent> buffer = new ArrayBlockingQueue<>(bufferSize); return new CustomCacheAppender(name, buffer); } }
- 自定义Appender配置方式:
<!-- Configuration标签增加packages属性,指定自定义Appender所在包路径,才能识别自定义Appender的@Plugin注解 --> <Configuration status="WARN" packages="com.your.project.package"> <Appenders> <CustomCacheAppender name="MyCache" bufferSize="10"/> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="MyCache"/> </Root> </Loggers> </Configuration>
注意:无特殊定制需求时优先使用官方提供的
CyclicBufferAppender,无需维护自定义代码,性能和稳定性有官方保障。
内容的提问来源于stack exchange,提问作者keiki
相关产品推荐
相关产品推荐

