Redirect System.out.println to log

2019-02-16 18:06发布

In my project test suite there is big usage of

System.out.println 

I'm trying to redirect these output to log file (through configuration or from single point without refactoring whole project ) so that can be disabled when necessary to improve performance. I'm using log4j for logging. Does any one know is this possible ? if so how to do it ? Thanks in advance.

标签: java logging
5条回答
我欲成王,谁敢阻挡
2楼-- · 2019-02-16 18:47

Given that it's better replace the System.out.println(), sometimes we have no choice. Anyway I've made a little utility for that:

SystemOutToSlf4j.enableForClass(MyClass.class)

Then all the println originated from MyClass will be redirected to the logger. 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;
  }

}
查看更多
萌系小妹纸
3楼-- · 2019-02-16 18:50
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);
}
}
查看更多
4楼-- · 2019-02-16 18:59

I think you can use System.setOut(PrintStream) to set your output to a file output stream. Then you can put this line in your BeforeClass method. I like to use a BaseTest class and put this line of code in the beforeclass method of that class. Then make all test cases extend this cclass.

查看更多
走好不送
5楼-- · 2019-02-16 19:02

My suggestion would be to refactor if possible. For a possible solution, check these similar questions

log4j redirect stdout to DailyRollingFileAppender

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

查看更多
\"骚年 ilove
6楼-- · 2019-02-16 19:02

Use shell redirection. Figure out the "java" invocation for your project, if you're on most vaguely UNIX-like systems, ps aux | grep java will help.

Then just run this command with > /path/to/logfile. Example:

java -jar myjar.jar -cp path/to/lib.jar:path/to/otherlib.jar com.bigcorp.myproject.Main > /var/log/myproject.log
查看更多
登录 后发表回答