log4j rediriger stdout vers DailyRollingFileAppender

j'ai une application java qui utilise 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

donc toutes les déclarations de log sont correctement annexées au fichier, mais je perds stdout et stderr. Comment puis-je rediriger les traces de pile d'exception et les sysouts vers le fichier quotidien roulé ?

58
demandé sur letronje 2009-07-29 17:15:45

9 réponses

// 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);
            }
        };
    }
}
93
répondu Michael S. 2009-09-02 20:59:26

en code Skaffman : pour supprimer les lignes vides dans les logs log4j, il suffit d'ajouter la méthode "println" à 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);
        }
    };
}
10
répondu Fizou 2012-09-26 06:13:32

J'ai repris L'idée de Michael S., Mais comme mentionné dans un commentaire, il a quelques problèmes: il ne capture pas tout, et il imprime des lignes vides.

aussi je voulais séparer System.out et System.err , de sorte que System.out soit enregistré avec le niveau de log 'INFO' et System.err soit enregistré avec 'ERROR' (ou 'WARN' si vous voulez).

donc c'est ma solution: D'abord une classe qui s'étend OutputStream (il est plus facile de passer outre toutes les méthodes pour OutputStream que pour PrintStream ). Il se connecte avec un niveau de log spécifié et copie tout à un autre OutputStream . Et aussi il détecte les chaînes "vides" (contenant uniquement des espaces) et ne les enregistre pas.

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

et ensuite une classe d'utilité très simple pour définir out et 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)));
}

}
7
répondu Dario Seidl 2011-09-20 01:20:24

si vous utilisez un serveur d'application, un conteneur servlet ou quelque chose de similaire, voir réponse de kgiannakakis .

pour les applications autonomes, voir ce . Vous pouvez reassing stdin , stdout et stderr en utilisant java.lang.Système classe. Fondamentalement, vous créez une nouvelle sous-classe de PrintStream et réglez cette instance sur System.hors.

quelque chose dans ce sens au début de votre application (non testé).

// 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());
5
répondu Juha Syrjälä 2017-05-23 12:26:10

je présume que vous enregistrez des stacktraces via e.printStackTrace() ? Vous pouvez passer un objet exception dans les méthodes de journalisation Log4j et celles-ci apparaîtront dans votre Logger (voir Logger.error (Object obj, Throwable t) )

Notez que vous pouvez changer de Système.et du Système.err to another PrintStream that redirects to Log4j. Ce serait un changement simple et vous sauver en convertissant tous vos System.out.println() énoncés.

2
répondu Brian Agnew 2009-07-29 13:22:59

les réponses ci-dessus donnent une excellente idée d'utiliser le proxy pour la journalisation STDOUT/ERR. Toutefois, les exemples de mise en œuvre fournis ne fonctionnent pas bien dans tous les cas. Par exemple, essayez

Système

.hors.printf("Testing %s\n", "ABC");

le code des exemples ci-dessus coupera la sortie en morceaux séparés sur une console et dans plusieurs entrées Log4j non lisibles.

la solution est de tamponner le sortie jusqu'à ce qu'un trigger '\n' soit trouvé à la fin du buffer. Parfois, le tampon se termine par "\r\n'. La classe ci-dessous aborde cette question. Il est entièrement fonctionnel. Appeler la méthode statique bind() pour l'activer.

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

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

// Based on
// /q/log4j-redirect-stdout-to-dailyrollingfileappender-23081/"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  

}
2
répondu MarkG 2015-01-14 19:04:51

flux de sortie et d'erreur Standard sont gérés à partir de votre conteneur. Par exemple, Tomcat utilise JULI pour enregistrer les flux de sortie et d'erreur.

je recommande de les laisser telles quelles. Évitez D'utiliser le système.hors.Imprimez dans votre demande. Voir ici pour les traces de pile.

1
répondu kgiannakakis 2009-07-29 13:26:28

l'anser de @Michael est un bon Point. Mais étendre PrintStream N'est pas très agréable, parce qu'il utilise une méthode interne void write(String) pour écrire toutes choses à un OutputStream.

je préfère utiliser la LoggingOutputStream Classe du paquet Log4J Contrib.

puis je redirige le système-streams comme ceci:

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);
    }
}
1
répondu itshorty 2012-06-18 10:43:23

avant d'utiliser le système.sera et du Système.méthode setErr nous devrions réinitialiser le java.util.journalisation.Objet LogManager en utilisant la méthode 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();
    }
}
0
répondu Vinoth Sakthivadivel 2017-05-07 13:20:38