将 System.out.println 记录到单个应用程序的日志文件

时间:2021-03-10 12:15:05

标签: java log4j tomcat9 system.out

我们在 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>

1 个答案:

答案 0 :(得分:1)

正如评论中所述,打印到 System.out 不是一种正确的记录方式,并且很难将发送到那里的数据重定向到适当的日志记录框架。

你有两个选择:

System.out 到 JULI 重定向

您可以将上下文的 swallowOutput 属性设置为 true(参见 documentation),这会将应用程序的标准输出和错误重定向到 JULI logging system

log4j2-appserver.jar 有一个 JULI 实现,它将所有日志发送到 Log4j2(参见 documentation)。

直接 System.out 到 Log4j 重定向

您可以像这样定义 PrintStreamLogger 适配器:

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]", LogPrintStream.class.getName(), cl.hashCode());
            }
            logger = LogManager.getLogger(name);
            classLoaderLoggers.put(cl, logger);
         }
         return logger;
      }
   }

   public LogPrintStream() {
      super(new LogOnFlush(), true);
   }
}

并在服务器启动的早期阶段使用它来替换 System.outSystem#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

相关问题