【问题标题】:Redirect System.out.println to log重定向 System.out.println 到日志
【发布时间】:2013-03-14 07:13:35
【问题描述】:

在我的项目测试套件中有大量使用

System.out.println 

我正在尝试将这些输出重定向到日志文件(通过配置或从单点而不重构整个项目),以便在必要时可以禁用以提高性能。我正在使用 log4j 进行日志记录。 有谁知道这可能吗?如果有怎么办? 提前致谢。

【问题讨论】:

标签: java logging


【解决方案1】:

鉴于最好替换System.out.println(),有时我们别无选择。无论如何,我为此做了一个小工具:

SystemOutToSlf4j.enableForClass(MyClass.class)

然后所有来自 MyClass 的 println 将被重定向到记录器。 See this post for more details...

public class SystemOutToSlf4j extends PrintStream {

  private static final PrintStream originalSystemOut = System.out;
  private static SystemOutToSlf4j systemOutToLogger;

  /**
   * Enable forwarding System.out.println calls to the logger if the stacktrace contains the class parameter
   * @param clazz
   */
  public static void enableForClass(Class clazz) {
    systemOutToLogger = new SystemOutToSlf4j(originalSystemOut, clazz.getName());
    System.setOut(systemOutToLogger);
  }


  /**
   * Enable forwarding System.out.println calls to the logger if the stacktrace contains the package parameter
   * @param packageToLog
   */
  public static void enableForPackage(String packageToLog) {
    systemOutToLogger = new SystemOutToSlf4j(originalSystemOut, packageToLog);
    System.setOut(systemOutToLogger);
  }

  /**
   * Disable forwarding to the logger resetting the standard output to the console
   */
  public static void disable() {
    System.setOut(originalSystemOut);
    systemOutToLogger = null;
  }

  private String packageOrClassToLog;

  private SystemOutToSlf4j(PrintStream original, String packageOrClassToLog) {
    super(original);
    this.packageOrClassToLog = packageOrClassToLog;
  }

  @Override
  public void println(String line) {
    StackTraceElement[] stack = Thread.currentThread().getStackTrace();
    StackTraceElement caller = findCallerToLog(stack);
    if (caller == null) {
      super.println(line);
      return;
    }

    org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(caller.getClass());
    log.info(line);
  }

  public StackTraceElement findCallerToLog(StackTraceElement[] stack) {
    for (StackTraceElement element : stack) {
      if (element.getClassName().startsWith(packageOrClassToLog))
        return element;
    }

    return null;
  }

}

【讨论】:

  • 在方法println(String)中调用getLogger(caller.getClass())应该替换为getLogger(caller.getClassName()),否则为SLF4J配置的日志框架会打印StackTraceElement的类名。
【解决方案2】:

如果可能,我的建议是重构。 如需可能的解决方案,请查看这些类似问题

log4j redirect stdout to DailyRollingFileAppender

Redirect System.out.println to Log4J, while keeping class name information

【讨论】:

    【解决方案3】:

    我认为您可以使用System.setOut(PrintStream) 将您的输出设置为文件输出流。然后你可以把这一行放在你的 BeforeClass 方法中。我喜欢使用 BaseTest 类,并将这行代码放在该类的 beforeclass 方法中。然后让所有的测试用例扩展这个cclass。

    【讨论】:

      【解决方案4】:

      使用外壳重定向。找出你的项目的“java”调用,如果你在最模糊的类 UNIX 系统上,ps aux | grep java 会有所帮助。

      然后只需使用 > /path/to/logfile 运行此命令。示例:

      java -jar myjar.jar -cp path/to/lib.jar:path/to/otherlib.jar com.bigcorp.myproject.Main > /var/log/myproject.log
      

      【讨论】:

      • 不是问题。
      【解决方案5】:
      public class RecursiveLogging {
      public static void main(String[] args) {
          System.setOut(new PrintStream(new CustomOutputStream()));
      
          TestMyException.testPrint();
      }
      
      }
      
      class CustomOutputStream extends OutputStream {
      private Logger logger = Logger.getLogger(this.getClass());
      
      @Override
      public final void write(int b) throws IOException {
          // the correct way of doing this would be using a buffer
          // to store characters until a newline is encountered,
          // this implementation is for illustration only
          logger.info((char) b);
      }
      
      @Override
      public void write(byte[] b, int off, int len) throws IOException {
          if (b == null) {
              throw new NullPointerException();
          } else if ((off < 0) || (off > b.length) || (len < 0) ||
                     ((off + len) > b.length) || ((off + len) < 0)) {
              throw new IndexOutOfBoundsException();
          } else if (len == 0) {
              return;
          }
          byte[] pb = new byte[len];
          for (int i = 0 ; i < len ; i++) {
              pb[i] = (b[off + i]);
          }
          String str = new String(pb);
          logger.info(str);
      }
      }
      

      【讨论】:

        【解决方案6】:

        我的解决方案非常简单,支持所有 PrintStream 功能,而不会重载所有内容。仅重载 flush(),因为它由 PrintStream 方法在每个新行中调用。

        public class ConsoleToLogger
        {
          private Logger log;
          private PrintStream originalStream;
          private Level logLevel;
          private ByteArrayBufferOutStream buffer;
          private PrintStream bufferPrintStream;
        
          ConsoleToLogger(PrintStream realPrintStream, Level pLogLevel)
          {
            buffer = new ByteArrayBufferOutStream();
            bufferPrintStream = new PrintStream(buffer);
            originalStream = realPrintStream;
            logLevel = pLogLevel;
            log = Logger.getLogger(Level.ERROR.equals(pLogLevel) ? "STDERR" : "STDOUT");
          }
        
          public PrintStream getPrintStream()
          {
            return bufferPrintStream;
          }
        
          private class ByteArrayBufferOutStream
            extends ByteArrayOutputStream
          {
            @Override
            public void flush()
              throws IOException
            {
              super.flush();
              String message = buffer.toString();
              originalStream.println(message);
              log.log(logLevel, message);
              buffer.reset();
            }
          }
        }
        
        
        // assign to System.out and system.err
        System.setOut(new ConsoleToLogger(System.out, Level.INFO).getPrintStream());
        System.setErr(new ConsoleToLogger(System.err, Level.ERROR).getPrintStream());
        
        

        【讨论】:

          猜你喜欢
          • 2011-03-14
          • 2023-03-09
          • 1970-01-01
          • 2020-04-23
          • 2017-03-08
          • 2014-04-10
          • 2018-02-16
          • 1970-01-01
          相关资源
          最近更新 更多