重定向 System.out.println
Posted
技术标签:
【中文标题】重定向 System.out.println【英文标题】:Redirect System.out.println 【发布时间】:2011-03-14 19:03:06 【问题描述】:我的应用程序有许多 System.out.println() 语句。
我想从 println 捕获消息并将它们发送到标准记录器(Log4j、JUL 等)。
怎么做?
【问题讨论】:
Removing access to System.out in java 的可能重复项 虽然在可能的欺骗的答案中有交叉,但我认为它不够接近值得匹配。更多的是寻求完全阻止输出的方法 - 这个是用于在其他地方捕获它。 【参考方案1】:System 类有一个setOut
和setErr
,可用于将输出流更改为,例如,带有支持File
的新PrintStream
,或者在这种情况下,可能是另一个流使用您选择的日志记录子系统。
请记住,如果您将日志库配置为输出到标准输出或错误(可能是无限递归类型),您很可能会遇到麻烦。
如果是这种情况,您可能只想将 System.out.print
-type 语句替换为真正的日志调用。
【讨论】:
为了使您的解决方案完整,我建议您添加有关如何在拦截所需的所有内容后恢复到正常标准输出的说明,即 System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out) )); @VicSeedoubleyew 存储原始的PrintStream
并在之后恢复它不是更好——如果其他东西也在重定向System.out
,这不一定会正确恢复它。跨度>
【参考方案2】:
我曾经有过类似的需求。我需要拦截一些第 3 方组件的输出并对错误消息做出反应。 这个概念是这样的:
private class Interceptor extends PrintStream
public Interceptor(OutputStream out)
super(out, true);
@Override
public void print(String s)
//do what ever you like
super.print(s);
public static void main(String[] args)
PrintStream origOut = System.out;
PrintStream interceptor = new Interceptor(origOut);
System.setOut(interceptor);// just add the interceptor
【讨论】:
您可能还需要覆盖println(String s)
方法。【参考方案3】:
更好的解决方案是检查并更改所有 println 语句以使用适当的日志库。您正在尝试做的是一个大黑客。
【讨论】:
也许他正试图将 System.out 重定向到 Logger ;-) +1: 日志框架的整个 point 是能够抽象出记录输出的位置,您应该将其日志记录方法称为“最终”输出从代码。更何况,重定向System.out
调用记录器出现 工作;直到有一天您将记录器配置为输出到标准输出,并且 bam - 堆栈溢出。
这并不难。 +1
我大体上同意,但正如我上面的回答中提到的,我需要拦截第 3 方 System.out 消息。没有其它的方法。因此,有一个用例。 GOTO 甚至还有一个用例 :-)【参考方案4】:
以下是如何将打印内容捕获到 System.out,然后按顺序放回原处:
// Start capturing
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
System.setOut(new PrintStream(buffer));
// Run what is supposed to output something
...
// Stop capturing
System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out)));
// Use captured content
String content = buffer.toString();
buffer.reset();
【讨论】:
【参考方案5】:扩展 PrintStream 是一个糟糕的解决方案,因为您必须覆盖所有 print()
和 println()
方法。相反,您可以捕获流:
public class ConsoleInterceptor
public interface Block
void call() throws Exception;
public static String copyOut(Block block) throws Exception
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(bos, true);
PrintStream oldStream = System.out;
System.setOut(printStream);
try
block.call();
finally
System.setOut(oldStream);
return bos.toString();
现在您可以像这样捕获它:
String result = ConsoleInterceptor.copyOut(() ->
System.out.print("hello world");
System.out.print('!');
System.out.println();
System.out.println("foobar");
);
assertEquals("hello world!\nfoobar\n", result);
【讨论】:
不错的可重用解决方案。另外我认为 Block 接口可以替换为 Runnable。【参考方案6】:我应用了这个的基本思想,它工作得很好。 无需更改所有 System.out.### 和 System.err.### 的东西。
import java.io.OutputStream;
import java.io.PrintStream;
public class Interceptor extends PrintStream
/** the logger */
private Logger log;
/** the origin output stream */
PrintStream orig;
/**
* Initializes a new instance of the class Interceptor.
*
* @param out the output stream to be assigned
* @param log the logger
*/
public Interceptor( OutputStream out, Logger log )
super( out, true );
this.log = log;
/**
* @inheritDoc
*/
@Override
protected void finalize() throws Throwable
detachOut();
super.finalize();
/**
* @inheritDoc
*/
@Override
public void print( String s )
//do what ever you like
orig.print( s );
log.logO( s, true );
/**
* @inheritDoc
*/
@Override
public void println( String s )
print( s + Defines.LF_GEN );
/**
* Attaches System.out to interceptor.
*/
public void attachOut()
orig = System.out;
System.setOut( this );
/**
* Attaches System.err to interceptor.
*/
public void attachErr()
orig = System.err;
System.setErr( this );
/**
* Detaches System.out.
*/
public void detachOut()
if( null != orig )
System.setOut( orig );
/**
* Detaches System.err.
*/
public void detachErr()
if( null != orig )
System.setErr( orig );
public class InterceptionManager
/** out */
private Interceptor out;
/** err */
private Interceptor err;
/** log */
private Logger log;
/**
* Initializes a new instance of the class InterceptionManager.
*
* @param logFileName the log file name
* @param append the append flag
*/
public InterceptionManager( String logFileName, boolean append )
log = new Logger();
log.setLogFile( logFileName, append );
this.out = new Interceptor( System.out, log );
this.out.attachOut();
this.err = new Interceptor( System.err, log );
this.err.attachErr();
/**
* @inheritDoc
*/
@Override
protected void finalize() throws Throwable
if( null != log )
log.closeLogFile();
super.finalize();
此视图行将启用日志记录,而无需进一步更改代码:
if( writeLog )
logFileName = this.getClassName() + "_Log.txt";
icMan = new InterceptionManager( logFileName, false );
System.out.format( "Logging to '%s'\n", logFileName );
【讨论】:
以上是关于重定向 System.out.println的主要内容,如果未能解决你的问题,请参考以下文章