log4j redirige stdout a DailyRollingFileAppender

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 

Entonces todas las instrucciones de registro se anexan correctamente al archivo, pero estoy perdiendo stdout y stderr. ¿Cómo puedo redirigir rastros de stack de excepción y sysouts al archivo rodado diariamente?

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

En el código de Skaffman: Para eliminar las líneas vacías en los registros de log4j, simplemente agregue el método “println” a PrintStream of 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); } }; } 

Recogí la idea de Michael S., pero como se menciona 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 , de modo que System.out se registra con el nivel de registro 'INFO' y System.err se registra con 'ERROR' (o 'WARN' si lo desea).

Así que esta es mi solución: primero una clase que amplía OutputStream (es más fácil anular todos los métodos para OutputStream que para PrintStream ). Se registra con un nivel de registro especificado y también copia todo a otro OutputStream . Y también detecta cadenas “vacías” (que contienen espacios en blanco solamente) 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 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, vea esto . Puede reasentar stdin , stdout y stderr usando la clase java.lang.System . Básicamente, usted crea una nueva subclase de PrintStream y establece esa instancia en System.out.

Algo en esta línea al inicio de su 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()); 

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

Tenga en cuenta que puede cambiar System.out y System.err a otro PrintStream que redirecciona a Log4j. Eso sería un cambio sencillo y le ahorrará la conversión de todas sus sentencias System.out.println() .

Las respuestas anteriores dan una excelente idea para usar 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 (“Probando% s \ n”, “ABC”);

El código de los ejemplos anteriores cortará la salida en piezas separadas en una consola y en múltiples entradas Log4j no legibles.

La solución es almacenar la salida hasta que se encuentre un disparador ‘\ n’ al final del búfer. Algunas veces el buffer termina con ‘\ r \ n’. La clase a continuación aborda este problema. Es completamente 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 } 

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

Mi recomendación es dejar esto como está. Evite usar System.out.print en su aplicación. Vea aquí para los rastros de stack.

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

Prefiero usar la clase LoggingOutputStream del paquete Log4J Contrib.

Luego redirijo las secuencias del sistema como esta:

 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, debemos restablecer el objeto java.util.logging.LogManager utilizando 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(); } }