Почему новый Java Logger не работает здесь постоянно? - PullRequest
0 голосов
/ 15 марта 2020

Обратите внимание, как работает большинство других вызовов Logger, и все System.out.println() работают, но, пожалуйста, кто-нибудь объяснит мне, почему Logger.info() звонки в stop() и destroy() никогда не печатаются с остальными журнала, так как эти функции явно работают !? С 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! ?‍♂️??");
        }
    }
}

OUTPUT с обоими 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! ?‍♂️??

OUTPUT с 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! ?‍♂️??

1 Ответ

2 голосов
/ 15 марта 2020

Эта проблема решена в: JDK-8161253 - LogManager $ Cleaner () может запретить вход в систему с другими перехватчиками отключения .

За билет:

В качестве обходного пути к созданию пользовательского обработчика завершения работы вы можете создать собственный обработчик и установить его на root регистраторе. Первое действие 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);
        }
    }
}
...