logdj redirect stdout para DailyRollingFileAppender

Eu tenho um aplicativo 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 

Portanto, todas as instruções de log são anexadas corretamente ao arquivo, mas estou perdendo stdout e stderr. Como faço para redirect os rastreamentos e os sysouts da pilha de exceção para o arquivo rolado 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); } }; } } 

No código Skaffman: Para remover linhas vazias nos logs log4j, basta adicionar o método “println” ao 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); } }; } 

Eu peguei a idéia do Michael S., mas como mencionado em um comentário, tem alguns problemas: ele não captura tudo e imprime algumas linhas vazias.

Também queria separar System.out e System.err , para que System.out seja logado com o nível de log 'INFO' e System.err seja logado com 'ERROR' (ou 'WARN' se você quiser).

Portanto, esta é a minha solução: primeiro, uma class que amplia o OutputStream (é mais fácil replace todos os methods do OutputStream que do PrintStream ). Ele registra com um nível de log especificado e também copia tudo para outro OutputStream . E também detecta strings “vazias” (contendo somente espaços em branco) e não as 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); } } 

E, em seguida, uma class de utilitário muito simples para definir e 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))); } } 

Se você estiver usando um servidor de aplicativos, um contêiner de servlet ou algo semelhante, veja a resposta de kgiannakakis .

Para aplicativos independentes, veja isto . Você pode reassing stdin , stdout e stderr usando a class java.lang.System . Basicamente, você cria uma nova subclass de PrintStream e configura essa instância para System.out.

Algo nesse sentido no início do seu aplicativo (não testado).

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

Eu presumo que você está registrando e.printStackTrace() via e.printStackTrace() ? Você pode passar um object de exceção para os methods de log Log4j e eles aparecerão no seu log (veja Logger.error (Object obj, Throwable t) )

Observe que você pode alterar System.out e System.err para outro PrintStream que redireciona para Log4j. Isso seria uma mudança direta e salvaria você convertendo todas as suas instruções System.out.println() .

As respostas acima dão uma excelente ideia para usar o proxy para o log de STDOUT / ERR. No entanto, os exemplos de implementação fornecidos não funcionam bem em todos os casos. Por exemplo, tente

System.out.printf (“Testando% s \ n”, “ABC”);

O código dos exemplos acima irá cortar a saída em partes separadas em um console e em várias inputs Log4j não legíveis.

A solução é armazenar em buffer a saída até que um acionador ‘\ n’ seja encontrado no final do buffer. Às vezes, o buffer termina com ‘\ r \ n’. A class abaixo aborda esse problema. É totalmente funcional. Chame o método estático bind () para ativá-lo.

 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 } 

A saída padrão e os streams de erro são gerenciados a partir do seu contêiner. Por exemplo, o Tomcat usa o JULI para registrar streams de saída e erro.

Minha recomendação é deixar isso como está. Evite usar o System.out.print no seu aplicativo. Veja aqui os rastreamentos de pilha.

O anser de @Michael é um bom ponto. Mas estender o PrintStream não é muito bom, porque ele usa um método interno void write(String) para gravar todas as coisas em um OutputStream.

Eu prefiro usar a class LoggingOutputStream do pacote Log4J Contrib.

Então eu redireciono os streams de sistema assim:

 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 o método System.setOut e System.setErr, devemos redefinir o object java.util.logging.LogManager usando o 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(); } }