2012-08-06 7 views
12

W niektórych okolicznościach muszę natychmiast wymusić opróżnienie w aplikacji wysyłającej logback. Znalazłem w docs ta opcja jest domyślnie włączona. Tajemniczo to nie działa. Jak widzę, w źródłach leżących u podstaw procesu jest prawidłowo wpisana BufferedOutputSream. Czy są jakieś problemy z BufferedOutputSream.flush()? Prawdopodobnie jest to raczej związane z kwestią płukania.Aplikator plików logback nie od razu się spłukuje.

Aktualizacja : Znalazłem problem w systemie Windows XP Pro SP 3 i systemie Red Hat Enterprise Linux Server w wersji 5.3 (Tikanga). użyłem tych libs:

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 brzmi:

<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> 

aktualizacja: Chciałbym zapewnić badanej jednostki, ale to nie wydaje się takie proste. Pozwolę sobie bardziej precyzyjnie opisać problem.

  1. Zdarzenie logowania wystąpił
  2. Event są przekazywane do pliku appender
  3. zdarzeń jest w odcinkach o określonej strukturze
  4. Serialized wiadomość imprezy zostanie przekazany do appender pliku i jest zamiar pisać do wyjścia stream
  5. Zapisywanie do strumienia jest zakończone, strumień wyjściowy jest przepłukiwany (sprawdziłem implementację pod kątem ). Zauważ, że immidiateFlush jest domyślnie ustawione na , więc metoda flush() jest wywoływana jawnie
  6. Brak wyniku w pliku!

Nieco później, po przepłynięciu bufora bazowego, zdarzenie pojawiło się w pliku. Pytanie brzmi: czy strumień wyjściowy gwarantuje natychmiastowy spłukanie?

Szczerze mówiąc, rozwiązałem już to, wprowadzając własne ImmediateRollingFileAppender, które wykorzystuje obiekt FileDescriptor natychmiastowej synchronizacji. Każdy zainteresowany może śledzić numer this.

To nie jest kwestia logback.

+1

Na jakim systemie operacyjnym to odkryłeś? –

+0

Opublikuj konfigurację logowania. – gresdiplitude

+0

Czy potrafisz zdefiniować "Tajemniczo to nie działa"? Zapewnienie testu jednostkowego byłoby bardzo przydatne. BTW, której wersji JDK używasz? – Ceki

Odpowiedz

8

Postanowiłem przynieść moje rozwiązanie wszystkim. Pozwól mi wyjaśnić przede wszystkim, że nie jest to problem z logback, a nie JRE. Jest to opisane w javadoc i generalnie nie powinno to stanowić problemu, dopóki nie spotkasz się z niektórymi oldschoolowymi rozwiązaniami integracyjnymi w zakresie synchronizacji plików.

Więc to jest appender logback realizowane natychmiast opróżnić:

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

} 

to odpowiada strumień wyjściowy klasę użytkową. Ze względu na niektóre metody i pola oryginalnego ResilientOutputStreamBase, które miały na celu rozszerzenie początkowo posiadały zmodyfikowane modyfikatory dostępu, musiałem zamiast tego rozszerzyć OutputStream i po prostu skopiować pozostałe i niezmienione z ResilientOutputStreamBase ido tego nowego.Właśnie wyświetlić zmieniony kod:

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

} 

i wreszcie 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> 
2

zrobiliście kawał dobrej roboty - dobrze zrobione. Oto propozycja, aby uzyskać bardziej zwięzły:

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

W konfiguracji trzeba użyć tego konkretnego nadajnika:

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

nie testowano. Prawdopodobnie wystąpią problemy z widocznością z polami, wymagające użycia pakietu logbackowego ch.qos.logback.core.recovery sam w sobie.

Przy okazji, zapraszam do submit a bug report do odesłania, aby uzyskać dodatkową opcję immediateSync na LayoutWrappingEncoder.

+0

Dzięki, Martin. Zrobię to jeden z tych dni. Dziękuję za utrzymanie nas i projektu. –

+2

Stworzyłem JIRA http://jira.qos.ch/browse/LOGBACK-735 –

+0

this outputStream jest ResilientFileOutputStream i nie jest rozszerzany z FileOutputStream, więc nie ma metody getFD(). – BlackJoker

0

Wiele monitorowania oprogramowania pośredniego znajduje nowe zdarzenia, sprawdzając aktualizacje sygnatury czasowej i rozmiaru pliku. Z tego powodu konieczne jest zarejestrowanie zdarzenia, zaktualizowanie sygnatury czasowej i rozmiaru pliku.

Ta klasa może rozwiązać go

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

zestaw klasę MyFileAppender dla appender.

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

Mam nadzieję, że to logback rozwiązuje ten problem.

+0

Mam problem z tym rozwiązaniem, ponieważ przerwane wątki "resilientFos.getChannel(). Force (true)" zamyka kanał plików i rejestrowanie zatrzymuje się później. – Sushant