Logback文件appender不会立即刷新

时间:2012-08-06 14:07:37

标签: java logging io logback flush

在某些情况下,我需要立即强制刷新logback的文件appender。我在docs找到了此选项默认情况下已启用。神秘地说这不起作用。正如我在源代码中看到的那样,正确处理BufferedOutputSreamBufferedOutputSream.flush()是否有任何问题?可能这与冲洗问题有关。

更新: 我在Windows XP Pro SP 3和Red Hat Enterprise Linux Server 5.3版(Tikanga)上发现了这个问题。 我使用了这些库:

jcl-over-slf4j-1.6.6.jar
logback-classic-1.0.6.jar
logback-core-1.0.6.jar
slf4j-api-1.6.6.jar

logback.xml是:

<configuration>
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>/somepath/file.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
            <fileNamePattern>file.log.%i</fileNamePattern>
            <minIndex>1</minIndex>
            <maxIndex>3</maxIndex>
        </rollingPolicy>
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <maxFileSize>5MB</maxFileSize>
        </triggeringPolicy>
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="debug">
        <appender-ref ref="FILE"/>
    </root>
</configuration>

更新 我提供单元测试,但似乎并不那么简单。 让我更清楚地描述这个问题。

  1. 记录事件发生
  2. 将事件传递到文件appender
  3. 使用已定义的模式序列化事件
  4. 事件的序列化消息传递给文件appender并且是 即将写出输出流
  5. 写入流已完成,输出流已刷新(我已经 检查了实施情况)。请注意immidiateFlush为真 默认情况下,显式调用方法flush()
  6. 没有结果文件!
  7. 稍后当一些底层缓冲区流动时,该事件出现在文件中。 所以问题是:输出流是否保证立即冲洗?

    说实话,我已经通过实现我自己的ImmediateRollingFileAppender来解决这个问题,该FileDescriptor利用了{{1}}即时同步的功能。任何感兴趣的人都可以关注this

    所以这不是一个回归问题。

3 个答案:

答案 0 :(得分:9)

我决定把我的解决方案带给每个人。 首先让我澄清一下,这不是一个回滚问题而不是JRE问题。这在javadoc中有所描述,在您通过文件同步面对一些旧的学校集成解决方案之前,通常不应该成为问题。

所以这是一个实现立即刷新的logback appender:

public class ImmediateFileAppender<E> extends RollingFileAppender<E> {

    @Override
    public void openFile(String file_name) throws IOException {
        synchronized (lock) {
            File file = new File(file_name);
            if (FileUtil.isParentDirectoryCreationRequired(file)) {
                boolean result = FileUtil.createMissingParentDirectories(file);
                if (!result) {
                    addError("Failed to create parent directories for [" + file.getAbsolutePath() + "]");
                }
            }

            ImmediateResilientFileOutputStream resilientFos = new ImmediateResilientFileOutputStream(file, append);
            resilientFos.setContext(context);
            setOutputStream(resilientFos);
        }
    }

    @Override
    protected void writeOut(E event) throws IOException {
        super.writeOut(event);
    }

}

这是相应的输出流实用程序类。由于原始ResilientOutputStreamBase的某些方法和字段最初应该包含已打包的访问修饰符,我必须扩展OutputStream而只是复制ResilientOutputStreamBase和{{1这个新的。我只显示更改的代码:

ResilientFileOutputStream

最后是配置:

public class ImmediateResilientFileOutputStream extends OutputStream {

    // merged code from ResilientOutputStreamBase and ResilientFileOutputStream

    protected FileOutputStream os;

    public FileOutputStream openNewOutputStream() throws IOException {
        return new FileOutputStream(file, true);
    }

    @Override
    public void flush() {
        if (os != null) {
            try {
                os.flush();
                os.getFD().sync(); // this's make sence
                postSuccessfulWrite();
            } catch (IOException e) {
                postIOFailure(e);
            }
        }
    }

}

答案 1 :(得分:2)

你做得很好 - 干得好。这是一个让它更简洁的建议:

public class ImmediateFlushPatternLayoutEncoder extends PatternLayoutEncoder {
    public void doEncode(ILoggingEvent event) throws IOException {
        super.doEncode(event);
        if (isImmediateFlush()) {
             if (outputStream.os instanceof FileOutputStream) {
                 ((FileOutputStream) outputStream.os).getFD().sync();
             }
        }
    }
}

在配置中,您必须使用该特定编码器:

<encoder class="ch.qos.logback.core.recovery.ImmediateFlushPatternLayoutEncoder">

未经测试。可能会出现字段的可见性问题,需要使用logback ch.qos.logback.core.recovery包本身

顺便说一句,我邀请您submit a bug report进行回访,以便在immediateSync上获得其他选项LayoutWrappingEncoder

答案 2 :(得分:0)

许多监控中间件通过检查时间戳和文件大小的更新来查找新事件。因此,需要记录事件的时间,更新时间戳和文件大小。

这门课可以解决它

public class MyFileAppender<E> extends FileAppender<E> {
    protected void writeOut(E event) throws IOException {
        super.writeOut(event);
        ResilientFileOutputStream resilientFos = (ResilientFileOutputStream) super.getOutputStream();
        resilientFos.flush();
        resilientFos.getChannel().force(true);
    }
}

为appender设置MyFileAppender类。

<appender name="FILE" class="MyFileAppender">

我希望这是logback解决了这个问题。