在我的项目测试套件中有大量使用
System.out.println
我正在尝试将这些输出重定向到日志文件(通过配置或从单点而不重构整个项目),以便在必要时可以禁用以提高性能。我正在使用 log4j 进行日志记录。有谁知道这可能吗?如果有怎么办?提前致谢。
鉴于它更好地替换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;
}
}
如果可能的话,我的建议是重构。有关可能的解决方案,请检查这些类似的问题
我认为您可以使用System.setOut(PrintStream)
将输出设置为文件输出流。然后你可以把这一行放在你的 BeforeClass 方法中。我喜欢使用 BaseTest 类,并将这行代码放在该类的 beforeclass 方法中。然后让所有的测试用例扩展这个cclass。
使用外壳重定向。找出你的项目的“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
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);
}
}
我的解决方案非常简单,并且支持所有 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());