Java System.setErr()会干扰记录器

Java System.setErr()会干扰记录器,java,java.util.logging,Java,Java.util.logging,在一个更大的程序中,我使用了一个静态的java.util.logging.Logger实例,但将System.err连续重定向到几个不同的文件。当我第二次尝试重定向系统时,记录器无法记录。错误 下面是一个测试程序来说明问题: import java.io.FileNotFoundException; import java.io.PrintStream; import java.util.logging.Logger; class TestRedirect { static final

在一个更大的程序中,我使用了一个静态的
java.util.logging.Logger
实例,但将
System.err
连续重定向到几个不同的文件。当我第二次尝试重定向系统时,
记录器
无法记录。错误

下面是一个测试程序来说明问题:

import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.logging.Logger;

class TestRedirect {
    static final Logger logger = Logger.getLogger("test");

    public static void main(String[] args) throws FileNotFoundException {
        for (int i = 1; i <= 2; i++) {
            TestRedirect ti = new TestRedirect();
            ti.test(i);
        }
    }

    void test(int i) throws FileNotFoundException {
        PrintStream filePrintStream = new PrintStream("test" + i + ".log");
        PrintStream stderr = System.err; // Save stderr stream.
        System.setErr(filePrintStream);  // Redirect stderr to file.
        System.err.println("about to log " + i);
        logger.info("at step " + i);
        System.setErr(stderr);           // Restore stderr stream.
        filePrintStream.close();
    }
}
test2.log:

about to log 1
Jan 28, 2014 4:34:20 PM TestRedirect test
INFO: at step 1
about to log 2

我想我会在test2.log中看到
Logger
生成的消息。为什么
记录器
停止工作,对此我能做些什么?

默认情况下,JRE配置为在根记录器上加载ConsoleHandler。默认情况下,日志消息将传递到根记录器处理程序。根记录器处理程序按需加载。在您当前的程序中,根记录器ConsoleHandler的延迟加载正在捕获您的第一个重定向System.err。在此之后,根记录器处理程序永远不会重新加载,这就是为什么您永远不会在日志2中看到日志消息的原因。加上第一个重定向的流是关闭的,所以现在根控制台句柄正在写入一个关闭的流

为了证明这一点,添加以下内容作为测试用例main方法的第一行,然后运行程序

Logger.getLogger("").getHandlers(); //Force load root logger handlers.
Logger.getLogger("").removeHandler((Handler) null);
您将看到,现在没有记录记录器消息。如果您想知道为什么这样做,可以阅读
java.util.logging.LogManager$RootLogger
源代码以了解详细信息

您需要做的是使用重定向的System.err流创建一个流,然后从记录器中创建和StreamHandler。您还可以在记录器配置上切换使用,以避免写入原始System.err

另一种可能的解决方案是查找所有ConsoleHandler实例。卸下并关闭所有这些部件。执行重新映射System.err。然后创建并连接新的控制台手柄


就JDK而言,ConsoleHandler和ErrorManager应该设计为使用从不重定向的。如果有其他可能的方法,我通常建议不要在Java中重定向标准流。是否确实需要使用
setErr()
?@Darkhogg:原始程序实际上使用了Groovy
SystemOutputCeptor
(基本上是基于
FilteredOutputStream
)的“T”将stdout和stderr定向到日志文件以及控制台。
SystemOutputInterceptor
在封面下调用
setErr()
。程序中的一些部分使用了记录器,一些只是将信息和错误写入到stderr和stdout。