2009-07-29 15 views
56

Ho un'app java che utilizza log4j.log4j redirect stdout a DailyRollingFileAppender

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 

Quindi tutte le dichiarazioni di log vengono correttamente allegato al file, ma sto perdendo stdout e stderr. Come faccio a reindirizzare le tracce dello stack delle eccezioni e i sysout al file di lavoro giornaliero?

risposta

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

funziona con eccezioni, ma non cattura tutto l'output scritto su stdout. Ad esempio se hai chiamato System.out.print (5); ignorerebbe il flusso di stampa proxy. pure, anche con eccezioni, questo finirà con la stampa di righe vuote aggiuntive su stderr se si esegue qualche eccezioneException.printStackTrace() – sbridges

+0

hi..where il file di registro verrà generato? grazie –

+3

@sbridge puoi semplicemente ignorare più metodi se necessario. Ad esempio, 'printStackTrace' chiama il metodo' println (Object o) 'che potresti anche sovrascrivere per eliminare quelle brutte righe vuote. – tysonjh

2

Suppongo che tu stia registrando gli stacktraces tramite e.printStackTrace()? È possibile passare un oggetto eccezione nei metodi di registrazione Log4j e quelli apparirà nel registro (vedi Logger.error(Object obj, Throwable t))

Si noti che è possibile modificare System.out e System.err ad un altro PrintStream che reindirizza a Log4j. Questo sarebbe un cambiamento semplice e ti farà risparmiare tutte le tue dichiarazioni System.out.println().

1

I flussi di errore e di output standard vengono gestiti dal contenitore. Ad esempio, Tomcat utilizza JULI per registrare output e flussi di errore.

La mia raccomandazione è di lasciarle così come sono. Evitare l'uso di System.out.print nella propria applicazione. Vedi here per le tracce dello stack.

+0

Che ne dici di applicazioni Java solide che non utilizzano un contenitore? Sicuramente c'è un modo per catturare lo stdout e loggarlo su log4j? –

+0

Come suggerito da Brian Agnew, è possibile reindirizzare lo stream a log4j. Comunque raccomando contro di esso. La soluzione non è stampare all'output standard, ma utilizzare log4j. Non esiste una singola libreria java decente che utilizza l'output standard. Se hai codice di terze parti che fa ciò, chiedi loro di usare log4j o il comando di registrazione. Il reindirizzamento dell'output standard dovrebbe essere l'ultima risorsa. – kgiannakakis

+0

Non utilizzare System.out.println() per la registrazione. Usa log4j (o un altro framework di registrazione) nel tuo codice e semplicemente fai in modo che la configurazione invii l'output a stdout. –

5

Se si utilizza un server applicazioni, un contenitore servlet o qualcosa di simile, vedere kgiannakakis's answer.

Per le app standalone, vedere this. È possibile riattaccare stdin, stdout e stderr utilizzando la classe java.lang.System. Fondamentalmente si crea una nuova sottoclasse di PrintStream e si imposta tale istanza su System.out.

Qualcosa su questa linea all'inizio della tua app (non testata).

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

ho preso l'idea da Michael S., ma, come menzionato in uno commento, ha alcuni problemi: non cattura tutto, e la stampa alcune righe vuote.

Inoltre ho voluto separare System.out e System.err, in modo che System.out ottiene registrato con il livello di log 'INFO' e System.err ottiene registrato con 'ERROR' (o 'WARN' se vi piace).

Quindi questa è la mia soluzione: In primo luogo una classe che estende OutputStream (è più facile per ignorare tutti i metodi per OutputStream che per PrintStream). Registra con un livello di registro specificato e copia anche tutto in un altro OutputStream. Inoltre rileva le stringhe "vuote" (contenenti solo spazi bianchi) e non le 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 poi una molto semplice classe di utilità per impostare out 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))); 
} 

} 
+0

Dove aggiungere il percorso al file di registro ?? – Aniket

+0

puoi darmi un'idea su come dovrebbe apparire il file log4j.properties o log4j.xml per farlo funzionare? – Aniket

+0

@acoolguy Vedere [Log4j XML Configuration Primer] (http: //wiki.apache.org/logging-log4j/Log4jXmlFormat), utilizzare un [FileAppender] (http://wiki.apache.org/logging-log4j/FileAppender). –

1

L'Anser di @ Michael è un buon punto. Ma estendere PrintStream non è molto bello, perché usa un metodo interno void write(String) per scrivere tutte le cose su un OutputStream.

Preferisco utilizzare lo LoggingOutputStream Class dal pacchetto Log4J Contrib.

Poi i reindirizzare il sistema-streams simili:

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

LoggingOutputStream non sembra più esistere nell'ultima versione di contributo di log4j (1.0.4) –

+0

basta prenderlo da https://svn.apache.org/viewvc/logging /log4j/trunk/contribs/JimMoore/LoggingOutputStream.java?view=co! è piuttosto isolato. – itshorty

10

In codice Skaffman: Per rimuovere righe vuote nei log log4j, basta aggiungere metodo "println" per PrintStream di 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); 
     } 
    }; 
} 
2

Le risposte di cui sopra danno un'idea eccellente per utilizzare il proxy per la registrazione STDOUT/ERR. Tuttavia, gli esempi di implementazione forniti non funzionano bene per tutti i casi. Ad esempio, prova

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

Il codice degli esempi precedenti taglia l'output in parti separate su una console e in più voci Log4j non leggibili.

La soluzione è di bufferizzare l'output fino a quando non viene trovato un trigger '\ n' alla fine del buffer. A volte il buffer termina con '\ r \ n'. La classe di seguito risolve questo problema. È completamente funzionante Chiama il metodo statico bind() per attivarlo.

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 

} 
0

Prima di utilizzare il metodo System.setOut e System.setErr dobbiamo resettare l'oggetto java.util.logging.LogManager utilizzando il metodo() 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(); 
    } 
} 
Problemi correlati