2012-08-06 11 views
12

Para algunas circunstancias, necesito forzar el vaciado inmediatamente en el appender de archivos de logback. He encontrado en docs esta opción está habilitada de forma predeterminada. Misteriosamente esto no funciona. Como veo en el proceso subyacente de fuentes implica BufferedOutputSream correctamente. ¿Hay algún problema con BufferedOutputSream.flush()? Probablemente esto esté más bien relacionado con el problema del enrojecimiento.El apéndice del archivo de inicio de sesión no se vacía inmediatamente

Actualización: Encontré el problema en Windows XP Pro SP 3 y en la versión 5.3 de Red Hat Enterprise Linux Server (Tikanga). utilicé estas librerías:

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 

El logback.xml es:

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

Actualizado: me gustaría proporcionar una prueba de unidad, pero eso no parece tan simple. Déjenme describir el problema más claramente.

  1. caso de la tala se produjo
  2. Evento traspasó appender archivo
  3. evento es de serie con el patrón definido
  4. mensaje serializado de eventos se pasa a la appender archivo y es punto de escribir a la salida corriente
  5. La escritura en la secuencia finaliza, la secuencia de salida se vacía (he comprobado la implementación). Tenga en cuenta que immidiateFlush es verdadero por predeterminado, por lo que el método flush() se invoca explícitamente
  6. ¡No hay resultados en el archivo!

Un poco más tarde, cuando fluyó un búfer subyacente, el evento aparece en el archivo. Entonces la pregunta es: ¿el flujo de salida garantiza una descarga inmediata?

Para ser sincero, ya he resuelto esto implementando mi propio ImmediateRollingFileAppender que aprovecha la facilidad de FileDescriptor de sincronización inmediata. Cualquier persona interesada puede seguir this.

Esto no es un problema de logback.

+1

¿En qué sistema operativo ha descubierto esto? –

+0

Publica tu configuración de inicio de sesión. – gresdiplitude

+0

¿Puedes definir "Misteriosamente esto no funciona"? Proporcionar una prueba unitaria sería muy útil. Por cierto, ¿qué versión del JDK estás usando? – Ceki

Respuesta

8

Decidí llevar mi solución a todos. Permítanme aclarar primero que esto no es un problema de logback y no es un problema de JRE. Esto se describe en el javadoc y, en general, no debería ser un problema hasta que se encuentre con algunas soluciones de integración de la vieja escuela sobre la sincronización de archivos.

Así que este es un appender logback implementado para eliminar de inmediato:

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

} 

Esta es correspondiente clase de utilidad flujo de salida. Debido a algunos métodos y campos del original ResilientOutputStreamBase que se suponía que se extendía inicialmente tenían modificadores de acceso empaquetados, tuve que extender OutputStream en su lugar y copiar el resto y sin cambios de ResilientOutputStreamBase y ResilientFileOutputStream a este nuevo.Acabo de mostrar el código cambiado:

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

} 

Y, finalmente, el 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

Has hecho un buen trabajo - bien hecho. Aquí es una propuesta para conseguir que sea más concisa:

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

En la configuración, usted tiene que usar ese codificador específica:

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

No se ha probado. Probablemente habrá problemas de visibilidad con los campos, requiriendo usar el paquete logback ch.qos.logback.core.recovery en sí mismo.

Por cierto, los invito a submit a bug report a logback para obtener una opción adicional immediateSync en LayoutWrappingEncoder.

+0

Gracias, Martin. Hará esto uno de estos días. Gracias por mantenernos a nosotros y al proyecto. –

+2

Ha creado un JIRA http://jira.qos.ch/browse/LOGBACK-735 –

+0

este outputStream es un ResilientFileOutputStream, y no se extiende desde FileOutputStream, por lo que no tiene el método getFD(). – BlackJoker

0

Muchos middleware de supervisión encuentran los nuevos eventos mediante verificación de actualizaciones de marca de tiempo y tamaño de archivo. Por este motivo, es necesario programar el momento en que se registra el evento, la marca de tiempo y el tamaño del archivo.

Esta clase podría resolverlo

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

conjunto de clases para MyFileAppender appender.

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

Espero que el logback resuelva este problema.

+0

Tengo un problema con esta solución, para hilos interrumpidos "resilientFos.getChannel(). Force (true)" cierra el canal de archivos y el registro se detiene a partir de entonces. – Sushant

Cuestiones relacionadas