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 instructions de journal sont correctement ajoutées au fichier, mais je perds stdout et stderr. Comment rediriger les traces de pile d'exceptions et les sysouts vers le fichier roulé quotidien?
// 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);
}
};
}
}
Dans le code Skaffman: pour supprimer les lignes vides dans les journaux log4j, ajoutez simplement 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);
}
};
}
J'ai repris l'idée de Michael S., mais comme mentionné dans un commentaire, cela a quelques problèmes: il ne capture pas tout et imprime des lignes vides.
Je voulais aussi séparer System.out
et System.err
, pour que System.out
se connecte avec le niveau de journal 'INFO'
et System.err
se connecte avec 'ERROR'
(ou 'WARN'
si tu veux).
Voici donc ma solution: d'abord une classe qui étend OutputStream
(il est plus facile de remplacer toutes les méthodes pour OutputStream
que pour PrintStream
). Il se connecte avec un niveau de journal spécifié et copie également tout dans un autre OutputStream
. Il détecte également 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 puis une classe 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 servlet ou quelque chose de similaire, voir réponse de kgiannakakis .
Pour les applications autonomes, voir this . Vous pouvez rassurer stdin , stdout et stderr en utilisant la classe Java.lang.System . Fondamentalement, 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 d'implémentation fournis ne fonctionnent pas correctement dans tous les cas. Par exemple, essayez
System.out.printf ("Test de% s\n", "ABC");
Le code des exemples ci-dessus coupera la sortie en morceaux séparés sur une console et en plusieurs entrées Log4j non lisibles.
La solution consiste à tamponner la sortie jusqu'à ce qu'un déclencheur '\ n' soit trouvé à la fin du tampon. Parfois, le tampon se termine par "\ r\n". La classe ci-dessous résout ce problème. Il 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 passer un objet d'exception dans les méthodes de journalisation Log4j et celles-ci apparaîtront 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 et vous éviter de convertir toutes vos instructions System.out.println()
.
Les flux de sortie et d'erreur standard 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.
L'anser 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 choses dans un OutputStream.
Je préfère utiliser la 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();
}
}