Pregunta

Tengo una aplicación java que usa log4j.

Config:

log4j.rootLogger=info, file

log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File=${user.home}/logs/app.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d [%t] %c %p %m%n

Así que todas las declaraciones de registro se adjuntan correctamente al archivo, pero estoy perdiendo stdout y stderr. ¿Cómo redirecciono los seguimientos y sysouts de la pila de excepciones al archivo enrollado diario?

¿Fue útil?

Solución

// I set up a ConsoleAppender in Log4J to format Stdout/Stderr
log4j.rootLogger=DEBUG, CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%t] %-5p %c - %m%n


// And I call this StdOutErrLog.tieSystemOutAndErrToLog() on startup

public class StdOutErrLog {

    private static final Logger logger = Logger.getLogger(StdOutErrLog.class);

    public static void tieSystemOutAndErrToLog() {
        System.setOut(createLoggingProxy(System.out));
        System.setErr(createLoggingProxy(System.err));
    }

    public static PrintStream createLoggingProxy(final PrintStream realPrintStream) {
        return new PrintStream(realPrintStream) {
            public void print(final String string) {
                realPrintStream.print(string);
                logger.info(string);
            }
        };
    }
}

Otros consejos

En el código Skaffman: para eliminar las líneas vacías en los registros log4j, simplemente agregue " println " método para PrintStream de createLoggingProxy

public static PrintStream createLoggingProxy(final PrintStream realPrintStream) {
    return new PrintStream(realPrintStream) {
        public void print(final String string) {
            logger.warn(string);
        }
        public void println(final String string) {
            logger.warn(string);
        }
    };
}

Tomé la idea de Michael S., pero como mencioné en un comentario, tiene algunos problemas: no captura todo e imprime algunas líneas vacías.

También quería separar System.out y System.err , para que System.out se registre con el nivel de registro 'INFO' y System.err se registran con 'ERROR' (o 'WARN' si lo desea).

Así que esta es mi solución: Primero, una clase que amplíe OutputStream (es más fácil anular todos los métodos para OutputStream que para PrintStream ). Registra con un nivel de registro especificado y también copia todo a otro OutputStream . Y también detecta " vacío " cadenas (que contienen solo espacios en blanco) y no las registra.

import java.io.IOException;
import java.io.OutputStream;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

public class LoggerStream extends OutputStream
{
private final Logger logger;
private final Level logLevel;
private final OutputStream outputStream;

public LoggerStream(Logger logger, Level logLevel, OutputStream outputStream)
{
    super();

    this.logger = logger;
    this.logLevel = logLevel;
    this.outputStream = outputStream;
}

@Override
public void write(byte[] b) throws IOException
{
    outputStream.write(b);
    String string = new String(b);
    if (!string.trim().isEmpty())
        logger.log(logLevel, string);
}

@Override
public void write(byte[] b, int off, int len) throws IOException
{
    outputStream.write(b, off, len);
    String string = new String(b, off, len);
    if (!string.trim().isEmpty())
        logger.log(logLevel, string);
}

@Override
public void write(int b) throws IOException
{
    outputStream.write(b);
    String string = String.valueOf((char) b);
    if (!string.trim().isEmpty())
        logger.log(logLevel, string);
}
}

Y luego una clase de utilidad muy simple para establecer out y err :

import java.io.PrintStream;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

public class OutErrLogger
{
public static void setOutAndErrToLog()
{
    setOutToLog();
    setErrToLog();
}

public static void setOutToLog()
{
    System.setOut(new PrintStream(new LoggerStream(Logger.getLogger("out"), Level.INFO, System.out)));
}

public static void setErrToLog()
{
    System.setErr(new PrintStream(new LoggerStream(Logger.getLogger("err"), Level.ERROR, System.err)));
}

}

Si está utilizando un servidor de aplicaciones, un contenedor de servlets o algo similar, consulte La respuesta de kgiannakakis .

Para aplicaciones independientes, consulte esto . Puede volver a ingresar stdin , stdout y stderr usando java.lang.System clase. Básicamente, creas una nueva subclase de PrintStream y configuras esa instancia en System.out.

Algo así en el inicio de la aplicación (no probado).

// PrintStream object that prints stuff to log4j logger
public class Log4jStream extends PrintStream {
      public void write(byte buf[], int off, int len) {
        try {
           // write stuff to Log4J logger
        } catch (Exception e) {
       }
    }
}

// reassign standard output to go to log4j
System.setOut(new Log4jStream());

Las respuestas anteriores dan una excelente idea para usar el proxy para el registro STDOUT / ERR. Sin embargo, los ejemplos de implementación proporcionados no funcionan bien para todos los casos. Por ejemplo, prueba

  

System.out.printf (" Pruebas% s \ n " ;, " ABC ");

El código de los ejemplos anteriores cortará la salida en partes separadas en una consola y en varias entradas de Log4j no legibles.

La solución es amortiguar la salida hasta que se encuentre un disparador '\ n' al final del búfer. A veces el búfer termina con '\ r \ n'. La siguiente clase aborda este problema. Es totalmente funcional. Llame al método estático bind () para activarlo.

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

// Based on
// http://stackoverflow.com/questions/1200175/log4j-redirect-stdout-to-dailyrollingfileappender
public class Log4jStdOutErrProxy {

  public static void bind() {
    bind(Logger.getLogger("STDOUT"), Logger.getLogger("STDERR"));
  }

  @SuppressWarnings("resource")
  public static void bind(Logger loggerOut, Logger loggerErr) {
    System.setOut(new PrintStream(new LoggerStream(loggerOut, Level.INFO,  System.out), true));
    System.setErr(new PrintStream(new LoggerStream(loggerErr, Level.ERROR, System.err), true));
  }

  private static class LoggerStream extends OutputStream {
    private final Logger logger;
    private final Level logLevel;
    private final OutputStream outputStream;
    private StringBuilder sbBuffer;

    public LoggerStream(Logger logger, Level logLevel, OutputStream outputStream) {
      this.logger = logger;
      this.logLevel = logLevel;
      this.outputStream = outputStream;
      sbBuffer = new StringBuilder();
    }

    @Override
    public void write(byte[] b) throws IOException {
      doWrite(new String(b));
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
      doWrite(new String(b, off, len));
    }

    @Override
    public void write(int b) throws IOException {
      doWrite(String.valueOf((char)b));
    }

    private void doWrite(String str) throws IOException {
      sbBuffer.append(str);
      if (sbBuffer.charAt(sbBuffer.length() - 1) == '\n') {
        // The output is ready
        sbBuffer.setLength(sbBuffer.length() - 1); // remove '\n'
        if (sbBuffer.charAt(sbBuffer.length() - 1) == '\r') {
          sbBuffer.setLength(sbBuffer.length() - 1); // remove '\r'
        }
        String buf = sbBuffer.toString();
        sbBuffer.setLength(0);
        outputStream.write(buf.getBytes());
        outputStream.write('\n');
        logger.log(logLevel, buf);
      }
    }
  } // inner class LoggerStream  

}

Supongo que está registrando apilados a través de e.printStackTrace () ? Puede pasar un objeto de excepción a los métodos de registro de Log4j y aparecerán en su registro (consulte Logger.error (objeto obj, Throwable t) )

Tenga en cuenta que puede cambiar System.out y System.err por otro PrintStream que redirige a Log4j. Eso sería un cambio sencillo y le ahorraría convertir todas sus declaraciones de System.out.println () .

La salida estándar y las secuencias de error se administran desde su contenedor. Por ejemplo, Tomcat utiliza JULI para registrar la salida y las secuencias de error.

Mi recomendación es dejar esto como está. Evite utilizar System.out.print en su aplicación. Consulte aquí para ver las huellas de la pila.

El anser de @Michael es un buen Punto. Pero extender PrintStream no es muy bueno, ya que utiliza un método interno void write (String) para escribir todas las cosas en un OutputStream.

Prefiero usar < código> LoggingOutputStream Class del paquete Log4J Contrib.

Luego redirecciono las secuencias del sistema de esta manera:

public class SysStreamsLogger {
    private static Logger sysOutLogger = Logger.getLogger("SYSOUT");
    private static Logger sysErrLogger = Logger.getLogger("SYSERR");

    public static final PrintStream sysout = System.out;
    public static final PrintStream syserr = System.err;

    public static void bindSystemStreams() {
        // Enable autoflush
        System.setOut(new PrintStream(new LoggingOutputStream(sysOutLogger, LogLevel.INFO), true));
        System.setErr(new PrintStream(new LoggingOutputStream(sysErrLogger, LogLevel.ERROR), true));
    }

    public static void unbindSystemStreams() {
        System.setOut(sysout);
        System.setErr(syserr);
    }
}

Antes de usar el método System.setOut y System.setErr, deberíamos restablecer el objeto java.util.logging.LogManager mediante el método reset ().

public static void tieSystemOutAndErrToLog() {

    try{

        // initialize logging to go to rolling log file
        LogManager logManager = LogManager.getLogManager();
        logManager.reset();

        // and output on the original stdout
        System.out.println("Hello on old stdout");
        System.setOut(createLoggingProxy(System.out));
        System.setErr(createLoggingProxy(System.err));

        //Following is to make sure whether system.out and system.err is redirecting to the stdlog.log file
        System.out.println("Hello world!");

        try {
            throw new RuntimeException("Test");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }catch(Exception e){
        logger.error("Caught exception at StdOutErrLog ",e);
        e.printStackTrace();
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top