问题描述
我们在 tomcat 上有多个应用程序,它们使用 System.out.println 语句记录到 catalina.out。 有一个应用程序会创建很多日志语句,因此我想将这些应用程序输出记录到单独的日志文件中。
我创建了一个 log4j.xml 设置,它只将 WARN 级别记录到 catalina.out。 但是 RollingFileAppender 还不能用于 System.out 语句,我不确定要更改什么。
我不想接触其他应用程序。这有可能吗?
<Configuration status="INFO">
<Appenders>
<Console name="stdout" target="SYstem_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36}: %msg%n" />
</Console>
<RollingFile
name="logFile"
fileName="../logs/app/log.log"
filePattern="../logs/app/log.%d{yyyy-MM-dd}.log.gz"
ignoreExceptions="false">
<PatternLayout>
<Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy interval="1"/>
</Policies>
<DefaultRolloverStrategy max="31" />
</RollingFile>
<Async name="logFile_async">
<AppenderRef ref="logFile"/>
</Async>
</Appenders>
<Loggers>
<Root level="WARN">
<AppenderRef ref="stdout" />
</Root>
<Logger name="my.company.logger" level="DEBUG">
<AppenderRef ref="logFile_async"/>
</Logger>
</Loggers>
</Configuration>
解决方法
正如评论中所述,打印到 System.out
不是一种正确的记录方式,并且很难将发送到那里的数据重定向到适当的日志记录框架。
你有两个选择:
System.out 到 JULI 重定向
您可以将上下文的 swallowOutput
属性设置为 true(参见 documentation),这会将应用程序的标准输出和错误重定向到 JULI logging system。
log4j2-appserver.jar
有一个 JULI 实现,它将所有日志发送到 Log4j2(参见 documentation)。
直接 System.out 到 Log4j 重定向
您可以像这样定义 PrintStream
到 Logger
适配器:
public class LogPrintStream extends PrintStream {
private static class LogOnFlush extends ByteArrayOutputStream {
// WeakHashMap so that we don't keep a reference to the classloaders.
protected final Map<ClassLoader,Logger> classLoaderLoggers = new WeakHashMap<>();
public LogOnFlush() {
// NOP
}
@Override
public void flush() {
final String message = toString();
if (message != null && message.length() > 0) {
Logger logger = getLogger();
logger.debug(message);
}
reset();
}
public Logger getLogger() {
final ClassLoader cl = Thread.currentThread().getContextClassLoader();
Logger logger = classLoaderLoggers.get(cl);
if (logger == null) {
final String name;
if (cl instanceof WebappClassLoaderBase) {
final WebappClassLoaderBase webCl = (WebappClassLoaderBase) cl;
name = String.format("%s.[%s].[%s]",LogPrintStream.class.getName(),webCl.getHostName(),webCl.getContextName());
} else {
name = String.format("%s.[%08x]",cl.hashCode());
}
logger = LogManager.getLogger(name);
classLoaderLoggers.put(cl,logger);
}
return logger;
}
}
public LogPrintStream() {
super(new LogOnFlush(),true);
}
}
并在服务器启动的早期阶段使用它来替换 System.out
到 System#setOut
。你可以,例如像这样使用 LifecycleListener
:
public class SystemOutReplaceListener implements LifecycleListener {
@Override
public void lifecycleEvent(LifecycleEvent event) {
if (Lifecycle.AFTER_INIT_EVENT.equals(event.getType())) {
System.setOut(new LogPrintStream());
}
}
}
警告:在本例中,System.out
的输出被发送到 Log4j2。必须小心,以免 Log4j2 将其日志转发到 System.out
。