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

2022-02-25 00:00:00 java tomcat9 log4j system.out

我们在Tomcat上有多个应用程序,它们使用System.out.println语句记录到Catalina.out。 只有一个应用程序会创建大量日志语句,因此我想将这些应用程序输出记录到单独的日志文件中。

我已经创建了一个log4j.xml设置,它只将警告级别记录到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重定向

您可以这样定义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

相关文章