System.setErr()干扰Logger

时间:2014-01-28 21:47:42

标签: java java.util.logging

在较大的程序中,我使用静态java.util.logging.Logger实例,但是将System.err连续重定向到几个不同的文件。第二次尝试重定向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();
    }
}

这是输出:

test1.log:

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

test2.log:

about to log 2

我以为我会在 test2.log 中看到Logger生成的消息。为什么Logger停止工作,我该怎么办呢?

1 个答案:

答案 0 :(得分:2)

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

为了证明这一点,请将以下内容添加为测试用例main方法的第一行并运行该程序。

Logger.getLogger("").getHandlers(); //Force load root logger handlers.
Logger.getLogger("").removeHandler((Handler) null);

您将看到现在没有记录任何记录器消息。如果您很好奇为什么会这样,您可以阅读java.util.logging.LogManager$RootLogger源代码以获取详细信息。

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

另一种可能的解决方案是找到所有ConsoleHandler实例。删除并关闭所有这些。执行重映射System.err。然后创建并附加新的ConsoleHandlers。

JDK明智地说,ConsoleHandler和ErrorManager应该被设计为使用从未重定向的java.io.FileDescriptor