java Logback 文件附加程序不会立即刷新

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11829922/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 06:33:07  来源:igfitidea点击:

Logback file appender doesn't flush immediately

javaloggingiologbackflush

提问by Viktor Stolbin

For some circumstances I need to force flushing in logback's file appender immediately. I've found in docsthis option is enabled by default. Mysteriously this doesn't work. As I see in the sources underlying process involves BufferedOutputSreamcorrectly. Is there any issues with BufferedOutputSream.flush()? Probably this is rather related to the flushing issue.

在某些情况下,我需要立即强制刷新 logback 的文件附加程序。我在文档中发现这个选项默认是启用的。神秘地这行不通。正如我在源代码中看到的,底层过程BufferedOutputSream正确涉及。有什么问题BufferedOutputSream.flush()吗?这可能与冲洗问题有关。

Update: I found the issue on Windows XP Pro SP 3 and on Red Hat Enterprise Linux Server release 5.3 (Tikanga). I used these libs:

更新:我在 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

The logback.xmlis:

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>

Updated:I'd provide a unit test but that seems not so simple. Let me describe the issue more clearly.

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

  1. Event of logging occurred
  2. Event is passed into file appender
  3. Event is serialized with defined pattern
  4. Serialized message of event is passed to the file appender and is about to write out to output stream
  5. Writing to the stream is finished, output stream is flushed (I've checked the implementation). Note that immidiateFlushis true by default so method flush()is invoked explicitly
  6. No result in the file!
  1. 发生日志事件
  2. 事件被传递到文件追加器
  3. 事件以定义的模式序列化
  4. 事件的序列化消息传递给文件追加器,即将写出到输出流
  5. 写入流完成,输出流被刷新(我已经检查了实现)。请注意,immidiateFlush默认情况下为 true,因此flush()显式调用方法
  6. 文件中没有结果!

A bit later when some underlying buffer was flowed the event appears in the file. So the question is: does output stream guarantee immediate flush?

稍后当一些底层缓冲区流动时,事件出现在文件中。所以问题是:输出流是否保证立即刷新?

To be honest I've already solve this by implementing my own ImmediateRollingFileAppenderthat leverages facility of FileDescriptorof immediate syncing. Anybody interested in can follow this.

老实说,我已经通过实现我自己的ImmediateRollingFileAppender利用FileDescriptor即时同步功能来解决这个问题。有兴趣的可以关注这个

So this is not a logback issue.

所以这不是 logback 问题。

采纳答案by Viktor Stolbin

I decided to bring my solution to everybody. Let me clarify first of all that this is not a logback issue and not a JRE problem. This is described in the javadocand generally shouldn't be an issue until you are faced with some old school integration solutions over the file syncing.

我决定把我的解决方案带给大家。首先让我澄清一下,这不是 logback 问题,也不是 JRE 问题。这在javadoc中进行了描述,并且通常不会成为问题,除非您遇到一些关于文件同步的老式集成解决方案。

So this is a logback appender implemented to flush immediately:

所以这是一个实现立即刷新的 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);
    }

}

This is corresponding output stream utility class. Because of some methods and fields of original ResilientOutputStreamBasethat was supposed for extending initially have packaged access modifiers I had to extend OutputStreaminstead and just copy the rest and unchanged of ResilientOutputStreamBaseand ResilientFileOutputStreamto this new one. I just display the changed code:

这是相应的输出流实用程序类。由于最初ResilientOutputStreamBase应该扩展的一些方法和原始字段包含打包的访问修饰符,我不得不扩展OutputStream,只需将其余的和不变的ResilientOutputStreamBase和不变的复制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);
            }
        }
    }

}

And finally the config:

最后是配置:

<appender name="FOR_INTEGRATION" class="package.ImmediateFileAppender">
    <file>/somepath/for_integration.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
        <fileNamePattern>for_integration.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} - %msg%n</pattern>
        <immediateFlush>true</immediateFlush>
    </encoder>
</appender>

回答by Yves Martin

You have done a good job - well done. Here is a proposal to get it more concise:

你做得很好——做得好。这是一个使它更简洁的建议:

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();
             }
        }
    }
}

In configuration, you have to use that specific encoder:

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

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

Not tested. Probably there will be visibility issues with fields, requiring to use logback ch.qos.logback.core.recoverypackage itself.

未测试。字段可能存在可见性问题,需要使用 logbackch.qos.logback.core.recovery包本身

By the way, I invite you to submit a bug reportto logback to get an additional option immediateSyncon LayoutWrappingEncoder.

顺便说一句,我邀请您向 logback提交错误报告以获得immediateSync关于LayoutWrappingEncoder.

回答by cotton

Many monitoring middleware find the new events by check for updates of time stamp and file size. For this reason, it need to time the event is recorded, time stamp and file size is updated.

许多监控中间件通过检查时间戳和文件大小的更新来发现新事件。为此,它需要对事件进行时间记录、时间戳和文件大小进行更新。

This class could solve it

这个类可以解决它

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);
    }
}

Set MyFileAppender class for appender.

为 appender 设置 MyFileAppender 类。

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

I hope that is logback solves this problem.

我希望 logback 可以解决这个问题。