Java 重定向System.out.println

Java 重定向System.out.println,java,logging,out,redirectstandardoutput,Java,Logging,Out,Redirectstandardoutput,我的应用程序有许多System.out.println()语句 我想捕获来自println的消息并将它们发送到标准记录器(Log4j、JUL等) 如何做到这一点?系统类有一个and,可用于将输出流更改为,例如,一个新的打印流,带有一个备份文件,或者,在这种情况下,可能是使用您选择的日志子系统的另一个流 请记住,如果您将日志库配置为输出到标准输出或错误(可能是无限递归类型),您可能会遇到麻烦 如果是这种情况,您可能只想用真正的日志调用替换System.out.print-type语句。更好的解决

我的应用程序有许多System.out.println()语句

我想捕获来自println的消息并将它们发送到标准记录器(Log4j、JUL等)

如何做到这一点?

系统类有一个and,可用于将输出流更改为,例如,一个新的
打印流
,带有一个备份
文件
,或者,在这种情况下,可能是使用您选择的日志子系统的另一个流


请记住,如果您将日志库配置为输出到标准输出或错误(可能是无限递归类型),您可能会遇到麻烦


如果是这种情况,您可能只想用真正的日志调用替换
System.out.print
-type语句。

更好的解决方案是检查并更改所有println语句,以使用正确的日志库。你要做的是一件大事。

我曾经有过类似的需求。我需要截获一些第三方组件的输出,并对错误消息作出反应。 概念如下所示:

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

我应用了这个基本思想,效果很好。 不需要更改所有的System.out.#####和System.err.##东西

此视图行将启用日志记录,而无需进一步更改代码:

  if( writeLog )
  {
    logFileName = this.getClassName() + "_Log.txt";
    icMan = new InterceptionManager( logFileName, false );
    System.out.format( "Logging to '%s'\n", logFileName );
  }

以下是如何将打印捕获到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();

扩展PrintStream是一个糟糕的解决方案,因为您必须覆盖所有
print()
println()
方法。相反,您可以捕获流:

公共类控制台接收器{
公共接口块{
void call()抛出异常;
}
公共静态字符串copyOut(块)引发异常{
ByteArrayOutputStream bos=新建ByteArrayOutputStream();
PrintStream PrintStream=新的PrintStream(bos,true);
PrintStream oldStream=System.out;
系统放样(打印流);
试一试{
block.call();
}
最后{
系统放样(oldStream);
}
返回bos.toString();
}
}
现在您可以像这样捕捉它:

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

可能他正试图将System.out重定向到记录器;-)+1:日志框架的全部要点是能够抽象记录输出的位置,您应该调用其日志方法作为代码的“最终”输出。更不用说,重定向
System.out
调用记录器似乎是可行的;直到有一天您将记录器配置为输出到标准输出和bam堆栈溢出,这并不是那么困难+1我大体上同意,但正如我在上面的回答中提到的,我需要截获第三方System.out消息。别无选择。因此,这有一个用例。GOTO甚至有一个用例:-)可能的重复虽然在可能的重复的答案中有交叉,但我认为它不够接近,不值得匹配。这更像是在寻求完全阻止输出的方法——这是在别处捕获输出的方法。为了使您的解决方案完整,我建议您添加有关如何在截获所有所需内容后恢复到正常标准输出的说明,即System.setOut(新的PrintStream(新的FileOutputStream(FileDescriptor.out))@Vicsedoubleyew存储原始的
打印流
并在之后还原不是更好吗?如果其他东西也重定向
System.out
,这不一定能正确还原。很好的可重用解决方案。另外,我认为块接口可以替换为Runnable。您可能还需要重写
println(String s)
方法。