问题描述
我正在尝试使用log4j2创建自定义日志附加程序。我的问题是我不想立即将日志写入文件追加器,而是之后。因此,理想情况下,我的spring boot应用程序应在某种数据结构中收集所有日志,然后在分批延迟3分钟后触发写入文件。 (我不应该使用spring batch,因为它不是批处理应用程序,而是简单的spring boot启动器)
解决方法
当我为Logback编写自定义提供程序时(作为Loki4j项目的一部分),我想到了一个线程安全的buffer的简洁实现,可以按批大小触发输出操作或自上次输出以来超时。
使用模式:
private static final LogRecord[] ZERO_EVENTS = new LogRecord[0];
private ConcurrentBatchBuffer<ILoggingEvent,LogRecord> buffer = new ConcurrentBatchBuffer<>(batchSize,LogRecord::create,(e,fl) -> eventFileLine(e,fl));
// somewhere in code where new log event arrives
var batch = buffer.add(event,ZERO_EVENTS);
if (batch.length > 0)
handleBatch(batch);
// somewhere in scheduled method that triggers every timeoutMs
var batch = buffer.drain(timeoutMs,ZERO_EVENTS);
if (batch.length > 0)
return handleBatch(batch).thenApply(r -> null);
// handling batches here
private void handleBatch(LogRecord[] lines) {
// flush to file
}
,
尝试查看 log4j2 附带的附加程序。它们中的大多数实现的功能类似于您所描述的。例如。 RandomAccessFileAppender
只有在从异步 appender 基础设施接收到完整的一批日志事件后才会写入文件。它们都共享封装此逻辑的 OutputStreamManager
或 FileManager
:
protected synchronized void write(final byte[] bytes,final int offset,final int length,final boolean immediateFlush) {
if (immediateFlush && byteBuffer.position() == 0) {
...
不幸的是,这似乎没有时间限制的解决方案。
我写了 log4j2 appender for Loki,它有自己的环形缓冲区和一个线程,当有足够的数据或用户指定的超时时间过去时,它将发送一批,here:
if (exceededBatchSizeThreshold() || exceededWaitTimeThreshold(currentTimeMillis)) {
try {
httpClient.log(outputBuffer);
} finally {
outputBuffer.clear();
timeoutDeadline = currentTimeMillis + batchWaitMillis;
}
}