2012-08-06 120 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