【问题标题】:Logging System.out.println to log file for single application将 System.out.println 记录到单个应用程序的日志文件
【发布时间】:2021-03-10 12:15:05
【问题描述】:

我们在一个 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 不是记录消息的正确方法,并且相当困难将其重定向到适当的记录框架。您混淆了方向:您将 Log4j2 配置为记录到 System.out,而您需要System.out 替换为将所有行发送到 Log4j2 的 PrintStream。这不是一个简单的操作。
  • @PiotrP.Karwasz 感谢您确认我的想法。在能够将所有 System.out 语句重构为 log4j 之前,它的目的是作为一种捷径。可能我们需要直接进行重构。

标签: java log4j tomcat9 system.out


【解决方案1】:

正如 cmets 中所述,打印到 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

【讨论】:

    猜你喜欢
    • 2017-01-29
    • 1970-01-01
    • 2011-07-11
    • 2016-02-26
    • 2011-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多