13

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

System.out.println 

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

4

6 回答 6

7

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

SystemOutToSlf4j.enableForClass(MyClass.class)

然后所有来自 MyClass 的 println 将被重定向到记录器。 有关详细信息,请参阅此帖子...

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;
  }

}
于 2015-09-05T09:23:49.467 回答
3

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

log4j 将标准输出重定向到 DailyRollingFileAppender

将 System.out.println 重定向到 Log4J,同时保留类名信息

于 2013-03-14T07:17:04.097 回答
3

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

于 2013-03-14T07:34:32.027 回答
1

使用外壳重定向。找出你的项目的“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
于 2013-03-14T07:19:48.663 回答
0
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);
}
}
于 2014-02-18T06:05:30.487 回答
0

我的解决方案非常简单,并且支持所有 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());

于 2021-08-26T15:28:17.780 回答