【问题标题】:Why is the new Java Logger failing to work consistently here?为什么新的 Java Logger 在这里无法始终如一地工作?
【发布时间】:2020-03-14 21:34:11
【问题描述】:

注意大多数其他调用 Logger 调用如何工作,所有 System.out.println() 工作,但请有人向我解释为什么 stop()destroy() 中的 Logger.info() 调用从不与其余调用一起打印日志,因为这些功能显然正在运行!?使用 java12,即使 destroy() 也没有显示。这是一个错误,我使用它是不是很奇怪,还是什么?

package test;
import java.util.logging.Logger;

public class Test {
    private static Logger LOGGER = Logger.getLogger(Test.class.getCanonicalName());

    public static void main(String[] args) {
        System.out.println("main()");
        LOGGER.info("main()");
        new Test();
    }

    private Test() {
        LOGGER.info("Test()");
        System.out.println("Test()");
        Runtime.getRuntime().addShutdownHook(new ShutdownThread());
    }

    public void shutdown() throws Exception {
        LOGGER.info("shutdown()");
        System.out.println("shutdown()");
        stop();
        destroy();
    }

    public void stop() throws Exception {
        LOGGER.info("stop()");
        System.out.println("stop()");
    }

    public void destroy() {
        LOGGER.info("destroy()");
        System.out.println("destroy()");
    }

    class ShutdownThread extends Thread {
        ShutdownThread() {
            super("app-shutdown-hook");
        }

        @Override
        public void run() {
            try {
                shutdown();
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("Bye! ????‍♂️????????");
        }
    }
}

同时使用 java 10 和 java 11 (OpenJDK) 的输出:

main()
Mar 14, 2020 1:53:59 PM test.Test main
INFO: main()
Mar 14, 2020 1:53:59 PM test.Test <init>
INFO: Test()
Test()
Mar 14, 2020 1:53:59 PM test.Test shutdown
INFO: shutdown()
shutdown()
stop()
destroy()
Bye! ????‍♂️????????

使用 java 12 (OpenJDK) 输出:

main()
Mar 14, 2020 2:17:13 PM test.Test main
INFO: main()
Mar 14, 2020 2:17:13 PM test.Test <init>
INFO: Test()
Test()
shutdown()
stop()
destroy()
Bye! ????‍♂️????????

【问题讨论】:

标签: java java.util.logging


【解决方案1】:

这个问题在:JDK-8161253 - LogManager$Cleaner() can prevent logging in other shutdown hooks.

按票:

作为创建自定义关闭挂钩的一种解决方法,您可以创建自定义处理程序并将其安装在根记录器上。 LogManager$Cleaner 的第一个操作是关闭记录器上所有已安装的处理程序。 一旦清洁器在自定义处理程序上调用关闭,您就可以执行以下操作之一:

  1. 让清理程序在处理程序中运行您的关闭代码。
  2. 使用 Thread API 找到您的自定义关闭挂钩并加入其中。

这是解决方案#1:

import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

public class Test {
    private static Logger LOGGER = Logger.getLogger(Test.class.getCanonicalName());

    public static void main(String[] args) {
        System.out.println("main()");
        LOGGER.info("main()");
        new Test();
    }

    private Test() {
        LOGGER.info("Test()");
        System.out.println("Test()");
        addShutdownHandler();
    }

    private void addShutdownHandler() {
        Logger root = Logger.getLogger("");
        Handler[] handlers = root.getHandlers();

        for(Handler h : handlers) {
            if (h.getClass() == ShutdownHandler.class) {
                return;
            }
        }

        for(Handler h : handlers) {
            root.removeHandler(h);
        }

        root.addHandler(new ShutdownHandler());

        for(Handler h : handlers) {
            root.addHandler(h);
        }
    }

    public void shutdown() throws Exception {
        LOGGER.info("shutdown()");
        System.out.println("shutdown()");
        stop();
        destroy();
    }

    public void stop() throws Exception {
        LOGGER.info("stop()");
        System.out.println("stop()");
    }

    public void destroy() {
        LOGGER.info("destroy()");
        System.out.println("destroy()");
    }

    class ShutdownHandler extends Handler {
        ShutdownHandler() {
        }

        @Override
        public void close() {
            final Thread t = Thread.currentThread();
            final String old = t.getName();
            t.setName("app-shutdown-hook");
            try {
                shutdown();
                System.out.println("Bye! ?‍♂️??");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                t.setName(old);
            }
        }

        @Override
        public void flush() {
        }

        @Override
        public void publish(LogRecord r) {
            isLoggable(r);
        }
    }
}

解决方案 #2 变得棘手,因为我们无法确保从一个关闭挂钩启动另一个关闭挂钩。如果您想使用 Thread::join,这意味着额外的编码。因此,为了解决这个问题,我们只需使用 Future API:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.logging.ErrorManager;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

public class Test {
    private static Logger LOGGER = Logger.getLogger(Test.class.getCanonicalName());

    public static void main(String[] args) {
        System.out.println("main()");
        LOGGER.info("main()");
        new Test();
    }

    private Test() {
        LOGGER.info("Test()");
        System.out.println("Test()");
        addShutdownHandler();
    }

    private void addShutdownHandler() {
        Logger root = Logger.getLogger("");
        Handler[] handlers = root.getHandlers();

        for(Handler h : handlers) {
            if (h.getClass() == CleanerJoin.class) {
                return;
            }
        }

        for(Handler h : handlers) {
            root.removeHandler(h);
        }

        root.addHandler(new CleanerJoin());

        for(Handler h : handlers) {
            root.addHandler(h);
        }
    }

    public void shutdown() throws Exception {
        LOGGER.info("shutdown()");
        System.out.println("shutdown()");
        stop();
        destroy();
    }

    public void stop() throws Exception {
        LOGGER.info("stop()");
        System.out.println("stop()");
    }

    public void destroy() {
        LOGGER.info("destroy()");
        System.out.println("destroy()");
    }

    class ShutdownTask implements Callable<Void> {
        ShutdownTask() {
        }

        @Override
        public Void call() throws Exception {
            shutdown();
            System.out.println("Bye! ?‍♂️??");
            return null;
        }
    }

    class CleanerJoin extends Handler {
        private final FutureTask<Void> sdt = new FutureTask<>(new ShutdownTask());

        CleanerJoin() {
            Runtime.getRuntime().addShutdownHook(new Thread(sdt, "app-shutdown-hook"));
        }


        @Override
        public void close() {
            boolean interrupted = false;
            try {
                for(;;) {
                    try { //Could use LogManager to lookup timeout values and use a timed join.
                        sdt.get();
                        break;
                    } catch (ExecutionException e) {
                        reportError("Shutdown hook failed.", e, ErrorManager.CLOSE_FAILURE);
                        break;
                    } catch (InterruptedException retry) {
                        interrupted = true;
                    }
                }
            } finally {
                if (interrupted) {
                    Thread.currentThread().interrupt();
                }
            }
        }

        @Override
        public void flush() {
        }

        @Override
        public void publish(LogRecord r) {
            isLoggable(r);
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-13
    • 1970-01-01
    • 2013-02-26
    • 2015-05-03
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多