我有一个使用log4j的java应用程序。
配置:
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
因此所有日志语句都正确地附加到文件中,但我丢失了stdout和stderr。如何将异常堆栈跟踪和sysout重定向到每日滚动文件?
答案 0 :(得分:96)
// 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);
}
};
}
}
答案 1 :(得分:10)
在Skaffman代码中:要删除log4j日志中的空行,只需将“println”方法添加到createLoggingProxy的PrintStream
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 :(得分:7)
我从迈克尔·S那里得到了这个想法,但是就像在一条评论中提到的那样,它有一些问题:它没有捕获所有内容,而是打印出一些空行。
此外,我想将System.out
和System.err
分开,以便System.out
记录日志级别'INFO'
,并System.err
记录'ERROR'
(或'WARN'
如果你愿意的话)。
所以这是我的解决方案:
首先是一个扩展OutputStream
的类(更容易覆盖OutputStream
的所有方法而不是PrintStream
)。它以指定的日志级别记录,并将所有内容复制到另一个OutputStream
。并且它还检测“空”字符串(仅包含空格)并且不记录它们。
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);
}
}
然后是一个非常简单的实用工具类来设置out
和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)));
}
}
答案 3 :(得分:5)
如果您使用的是应用程序服务器,servlet容器或类似内容,请参阅kgiannakakis's answer。
对于独立应用,请参阅this。您可以使用stdin课程重新定位stdout,stderr和java.lang.System。基本上,您创建了一个新的PrintStream子类,并将该实例设置为System.out。
应用程序启动时的这些内容(未经测试)。
// 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());
答案 4 :(得分:4)
上面的答案为使用代理进行STDOUT / ERR日志记录提供了一个很好的主意。但是,提供的实现示例对于所有情况都不能很好地工作。例如,尝试
System.out.printf("测试%s \ n"," ABC");
上面示例中的代码会将输出切换为控制台和多个不可读的Log4j条目中的单独部分。
解决方案是缓冲输出,直到触发' \ n'在缓冲区结束时找到。有时缓冲区以' \ r \ n \ n'结尾。 下面的课程解决了这个问题。它功能齐全。调用静态方法bind()来激活它。
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
}
答案 5 :(得分:2)
我认为你是通过e.printStackTrace()
记录堆栈跟踪的?您可以将异常对象传递给Log4j日志记录方法,这些方法将显示在您的日志中(请参阅Logger.error(Object obj, Throwable t))
请注意,您可以将System.out和System.err更改为重定向到Log4j的另一个PrintStream。这将是一个简单的更改,并保存您转换所有System.out.println()
语句。
答案 6 :(得分:1)
从容器管理标准输出和错误流。例如,Tomcat使用JULI记录输出和错误流。
我的建议是保留原样。避免在应用程序中使用System.out.print。有关堆栈跟踪,请参阅here。
答案 7 :(得分:1)
@迈克尔的缩影是一个好点。但扩展PrintStream并不是很好,因为它使用内部方法void write(String)
将所有内容写入OutputStream。
我更喜欢使用Log4J Contrib软件包中的LoggingOutputStream
Class。
然后我像这样重定向系统流:
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);
}
}
答案 8 :(得分:0)
在使用System.setOut和System.setErr方法之前,我们应该使用reset()方法重置java.util.logging.LogManager对象。
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();
}
}
答案 9 :(得分:0)
对于那些希望在log4j2中做到这一点的人。现在有一个组件可以为您创建这些流。
需要包括log4j-iostreams jar
参见:https://logging.apache.org/log4j/2.x/log4j-iostreams/index.html
示例:
def createPlayers():
players = []
player1Name = input("Please enter the first players name: ")
player1 = Player(player1Name)
players.append(player1)
player2Name = input("Please enter the second players name: ")
player2 = Player(player2Name)
players.append(player2)
print(players)
if numberOfPlayers == 3:
player3Name = input("Please enter the third players name: ")
player3 = Player(player3Name)
players.append(player3)
elif numberOfPlayers == 4:
player3Name = input("Please enter the third players name: ")
player3 = Player(player3Name)
players.append(player3)
player4Name = input("Please enter the fourth players name: ")
player4 = Player(player4Name)
players.append(player4)
else:
print("Please pick between 2 and 4 players.")
def drawGrid():
window.blit(boardImage,(0,0))
for x in range (height):
for y in range(width)::
if board[x][y] == player1.Pos:
pygame.draw.circle(window, PlayerOneColour, (x * squareSize + int(squareSize/2) -10, y*squareSize +int(squareSize/2)+6) ,charSize)
if board[x][y] == player2.Pos:
pygame.draw.circle(window, PlayerTwoColour, (x * squareSize + int(squareSize/2) +10, y*squareSize +int(squareSize/2)+6) ,charSize)
if numberOfPlayers == 3:
if board[x][y] == player3.Pos:
pygame.draw.circle(window, PlayerThreeColour, (x * squareSize + int(squareSize/2) +10, y*squareSize +int(squareSize/2)+6) ,charSize)
if numberOfPlayers == 4:
if board[x][y] == player4.Pos:
pygame.draw.circle(window, PlayerFourColour, (x * squareSize + int(squareSize/2) +10, y*squareSize +int(squareSize/2)+6) ,charSize)