Question

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

Ainsi, toutes les instructions du journal sont correctement ajouté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 roulé quotidien?

Était-ce utile?

La solution

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

Autres conseils

En code Skaffman: pour supprimer les lignes vides dans les journaux de log4j, ajoutez simplement "println". méthode à 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);
        }
    };
}

J'ai repris l'idée de Michael S., mais comme mentionné dans un commentaire, cela pose quelques problèmes: il ne capture pas tout et il affiche des lignes vides.

Je souhaitais également séparer System.out et System.err , afin que System.out soit consigné avec le niveau de journalisation 'INFO' et System.err est enregistré avec 'ERROR' (ou 'WARN' si vous le souhaitez).

Voici donc ma solution: Commencez par une classe qui étend OutputStream (il est plus facile de remplacer toutes les méthodes pour OutputStream que pour PrintStream ). Il enregistre avec un niveau de journalisation spécifié et copie également tout dans un autre OutputStream . Et aussi, il détecte " vide " chaînes (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'utilitaire 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)));
}

}

Si vous utilisez un serveur d'applications, un conteneur de servlets ou quelque chose de similaire, voir this . Vous pouvez réassumer stdin , stdout et stderr utilisant classe java.lang.System . En gros, vous créez une nouvelle sous-classe de PrintStream et définissez cette instance sur System.out.

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

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

  

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

Le code des exemples ci-dessus divisera la sortie en éléments séparés sur une console et en plusieurs entrées Log4j non lisibles.

La solution consiste à mettre en tampon la sortie jusqu'à ce qu'un déclencheur '\ n' se trouve à la fin du tampon. Parfois, le tampon se termine par '\ r \ n'. La classe ci-dessous aborde ce problème. C'est entièrement fonctionnel. Appelez 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
// 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  

}

Je suppose que vous enregistrez des traces de pile via e.printStackTrace () ? Vous pouvez transmettre un objet exception aux méthodes de journalisation Log4j. Celles-ci figureront dans votre journal (voir Logger.error (Object obj, Throwable t) )

Notez que vous pouvez remplacer System.out et System.err par un autre PrintStream qui redirige vers Log4j. Ce serait un changement simple qui vous éviterait de convertir toutes vos instructions System.out.println () .

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

Ma recommandation est de les laisser tels quels. Évitez d'utiliser System.out.print dans votre application. Voir ici pour les traces de pile.

La réponse de @Michael est un bon point. Mais étendre PrintStream n’est pas très agréable, car il utilise une méthode interne void write (String) pour écrire toutes les choses dans un OutputStream.

Je préfère utiliser le < code> Classe LoggingOutputStream du package Log4J Contrib.

Ensuite, je redirige les flux système 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);
    }
}

Avant d'utiliser les méthodes System.setOut et System.setErr, nous devons réinitialiser l'objet java.util.logging.LogManager à l'aide de 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();
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top