Domanda

Ho un'app Java che utilizza 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

Quindi tutte le istruzioni del registro sono state correttamente aggiunte al file, ma sto perdendo stdout e stderr. Come reindirizzare le tracce e i sysout dello stack di eccezioni al file con roll giornaliero?

È stato utile?

Soluzione

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

Altri suggerimenti

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

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

Inoltre volevo separare System.out e System.err , in modo che System.out venisse registrato con il livello di log 'INFO' e System.err vengono registrati con 'ERROR' (o 'WARN' se vuoi).

Quindi questa è la mia soluzione: Innanzitutto una classe che estende OutputStream (è più semplice sovrascrivere tutti i metodi per OutputStream che per PrintStream ). Registra con un livello di registro specificato e copia tutto su un altro OutputStream . E rileva anche "vuoto" stringhe (contenente 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 quindi una classe di utilità molto semplice 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)));
}

}

Se si utilizza un server applicazioni, un contenitore servlet o qualcosa di simile, consultare La risposta di kgiannakakis .

Per le app autonome, vedere questo . Puoi riesaminare stdin , stdout e stderr utilizzando classe java.lang.System . Fondamentalmente si crea una nuova sottoclasse di PrintStream e si imposta tale istanza su System.out.

Qualcosa del genere all'inizio della tua app (non testato).

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

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

  

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

Il codice degli esempi sopra taglierà 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 seguente affronta questo problema. È perfettamente 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  

}

Suppongo che stai registrando stacktraces tramite e.printStackTrace () ? È possibile passare un oggetto eccezione nei metodi di registrazione Log4j e questi verranno visualizzati nel registro (consultare Logger.error (Object obj, Throwable t) )

Nota che puoi cambiare System.out e System.err in un altro PrintStream che reindirizza a Log4j. Sarebbe una modifica semplice e ti risparmierebbe la conversione di tutte le tue istruzioni System.out.println () .

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

La mia raccomandazione è di lasciarli così come sono. Evita di utilizzare System.out.print nella tua applicazione. Vedi qui per le tracce dello stack.

La risposta di @Michael è un buon punto. Ma estendere PrintStream non è molto bello, perché usa un metodo interno void write (String) per scrivere tutto su un OutputStream.

Preferisco usare < code> LoggingOutputStream Class dal pacchetto Log4J Contrib.

Quindi reindirizzo i flussi di sistema in questo modo:

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

Prima di utilizzare il metodo System.setOut e System.setErr dovremmo ripristinare 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();
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top