2012-08-06 116 views
12

对于某些情况下,我需要立即强制的logback的文件附加器冲洗。我在docs发现这个选项是默认启用。神秘地这不起作用。正如我在源看到潜在的过程涉及BufferedOutputSream正确。 BufferedOutputSream.flush()有没有问题?可能这与冲洗问题有关。的logback文件的appender冲不立即

更新: 我发现这个问题在Windows XP专业版SP 3和Red Hat Enterprise Linux服务器上发布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. 事件被传递到文件附加器
  3. 事件被序列与定义的模式
  4. 事件的序列化消息被传递给文件附加器和是 即将写出到输出流
  5. 写入流完成后,输出流被刷新(我已经检查了执行 )。请注意,immidiateFlush为真 默认为明确调用
  6. 文件中无结果!

稍后,当某些基础缓冲区流动时,事件将出现在文件中。 所以,问题是:是否输出流保证立即冲洗?

说实话,我已经通过实施我自己的ImmediateRollingFileAppender解决了这个问题,它利用了即时同步的设备FileDescriptor。任何有兴趣的人都可以按照this

所以这不是一个logback问题。

+1

你发现了哪个操作系统? – 2012-08-09 06:41:50

+0

发布您的logback配置。 – gresdiplitude 2012-08-09 06:47:16

+0

你能否定义“神秘的这不起作用”?提供单元测试将非常有用。顺便说一下,你正在使用哪个版本的JDK? – Ceki 2012-08-09 15:41:26

回答

8

我决定把我的解决方案带给每个人。 让我澄清一下,首先的,这不是一个问题的logback而不是JRE问题。这在javadoc描述的,一般不应该是一个问题,直到你面对过的文件同步一些老学校的集成解决方案。

因此,这是实现以立即刷新的logback一个附加目的地:

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,而只是将其余部分和ResilientOutputStreamBaseResilientFileOutputStream的其余部分复制到这个新的。我只显示代码改变:

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

} 

最后的配置:

<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> 
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的logback对LayoutWrappingEncoder获得一个额外的选项immediateSync

+0

谢谢,马丁。这些日子会做这一件事。感谢您维护我们和项目。 – 2012-08-16 10:39:23

+2

已经创建了一个JIRA http://jira.qos.ch/browse/LOGBACK-735 – 2012-08-16 16:24:07

+0

这个outputStream是一个ResilientFileOutputStream,它不是从FileOutputStream扩展的,所以它没有getFD()方法。 – BlackJoker 2013-03-15 08:45:09

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

设置MyFileAppender类附加目的地。

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

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

+0

我正面临着这个解决方案的问题,对于中断线程“resilientFos.getChannel().force(true)”关闭了文件通道,之后记录停止。 – Sushant 2014-06-23 07:32:26