乐趣区

聊聊ElasticsearchUncaughtExceptionHandler

本文主要研究一下 ElasticsearchUncaughtExceptionHandler

ElasticsearchUncaughtExceptionHandler

class ElasticsearchUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {private static final Logger logger = LogManager.getLogger(ElasticsearchUncaughtExceptionHandler.class);

    @Override
    public void uncaughtException(Thread t, Throwable e) {if (isFatalUncaught(e)) {
            try {onFatalUncaught(t.getName(), e);
            } finally {
                // we use specific error codes in case the above notification failed, at least we
                // will have some indication of the error bringing us down
                if (e instanceof InternalError) {halt(128);
                } else if (e instanceof OutOfMemoryError) {halt(127);
                } else if (e instanceof StackOverflowError) {halt(126);
                } else if (e instanceof UnknownError) {halt(125);
                } else if (e instanceof IOError) {halt(124);
                } else {halt(1);
                }
            }
        } else {onNonFatalUncaught(t.getName(), e);
        }
    }

    static boolean isFatalUncaught(Throwable e) {return e instanceof Error;}

    void onFatalUncaught(final String threadName, final Throwable t) {logger.error(() -> new ParameterizedMessage("fatal error in thread [{}], exiting", threadName), t);
    }

    void onNonFatalUncaught(final String threadName, final Throwable t) {logger.warn(() -> new ParameterizedMessage("uncaught exception in thread [{}]", threadName), t);
    }

    void halt(int status) {AccessController.doPrivileged(new PrivilegedHaltAction(status));
    }

    static class PrivilegedHaltAction implements PrivilegedAction<Void> {

        private final int status;

        private PrivilegedHaltAction(final int status) {this.status = status;}

        @SuppressForbidden(reason = "halt")
        @Override
        public Void run() {
            // we halt to prevent shutdown hooks from running
            Runtime.getRuntime().halt(status);
            return null;
        }

    }

}
  • ElasticsearchUncaughtExceptionHandler 实现了 Thread.UncaughtExceptionHandler 接口
  • uncaughtException 方法首先判断 throwable 是否是 Error 类型,是的话则执行 onFatalUncaught(logger.error),然后执行 halt 方法,不是则执行 onNonFatalUncaught(logger.warn)
  • halt 方法 AccessController.doPrivileged 来执行对应 status 的 PrivilegedHaltAction,该 action 执行的是 Runtime.getRuntime().halt(status)

Bootstrap.init

elasticsearch-7.0.1/server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java

final class Bootstrap {

    private static volatile Bootstrap INSTANCE;
    private volatile Node node;
    private final CountDownLatch keepAliveLatch = new CountDownLatch(1);
    private final Thread keepAliveThread;
    private final Spawner spawner = new Spawner();

    //......

    static void init(
            final boolean foreground,
            final Path pidFile,
            final boolean quiet,
            final Environment initialEnv) throws BootstrapException, NodeValidationException, UserException {
        // force the class initializer for BootstrapInfo to run before
        // the security manager is installed
        BootstrapInfo.init();

        INSTANCE = new Bootstrap();

        final SecureSettings keystore = loadSecureSettings(initialEnv);
        final Environment environment = createEnvironment(pidFile, keystore, initialEnv.settings(), initialEnv.configFile());

        LogConfigurator.setNodeName(Node.NODE_NAME_SETTING.get(environment.settings()));
        try {LogConfigurator.configure(environment);
        } catch (IOException e) {throw new BootstrapException(e);
        }
        if (environment.pidFile() != null) {
            try {PidFile.create(environment.pidFile(), true);
            } catch (IOException e) {throw new BootstrapException(e);
            }
        }

        final boolean closeStandardStreams = (foreground == false) || quiet;
        try {if (closeStandardStreams) {final Logger rootLogger = LogManager.getRootLogger();
                final Appender maybeConsoleAppender = Loggers.findAppender(rootLogger, ConsoleAppender.class);
                if (maybeConsoleAppender != null) {Loggers.removeAppender(rootLogger, maybeConsoleAppender);
                }
                closeSystOut();}

            // fail if somebody replaced the lucene jars
            checkLucene();

            // install the default uncaught exception handler; must be done before security is
            // initialized as we do not want to grant the runtime permission
            // setDefaultUncaughtExceptionHandler
            Thread.setDefaultUncaughtExceptionHandler(new ElasticsearchUncaughtExceptionHandler());

            INSTANCE.setup(true, environment);

            try {
                // any secure settings must be read during node construction
                IOUtils.close(keystore);
            } catch (IOException e) {throw new BootstrapException(e);
            }

            INSTANCE.start();

            if (closeStandardStreams) {closeSysError();
            }
        } catch (NodeValidationException | RuntimeException e) {// disable console logging, so user does not see the exception twice (jvm will show it already)
            final Logger rootLogger = LogManager.getRootLogger();
            final Appender maybeConsoleAppender = Loggers.findAppender(rootLogger, ConsoleAppender.class);
            if (foreground && maybeConsoleAppender != null) {Loggers.removeAppender(rootLogger, maybeConsoleAppender);
            }
            Logger logger = LogManager.getLogger(Bootstrap.class);
            // HACK, it sucks to do this, but we will run users out of disk space otherwise
            if (e instanceof CreationException) {
                // guice: log the shortened exc to the log file
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                PrintStream ps = null;
                try {ps = new PrintStream(os, false, "UTF-8");
                } catch (UnsupportedEncodingException uee) {
                    assert false;
                    e.addSuppressed(uee);
                }
                new StartupException(e).printStackTrace(ps);
                ps.flush();
                try {logger.error("Guice Exception: {}", os.toString("UTF-8"));
                } catch (UnsupportedEncodingException uee) {
                    assert false;
                    e.addSuppressed(uee);
                }
            } else if (e instanceof NodeValidationException) {logger.error("node validation exception\n{}", e.getMessage());
            } else {
                // full exception
                logger.error("Exception", e);
            }
            // re-enable it if appropriate, so they can see any logging during the shutdown process
            if (foreground && maybeConsoleAppender != null) {Loggers.addAppender(rootLogger, maybeConsoleAppender);
            }

            throw e;
        }
    }

    //......
}
  • Bootstrap 的 init 静态方法使用 Thread.setDefaultUncaughtExceptionHandler(new ElasticsearchUncaughtExceptionHandler()) 来给线程设置 defaultUncaughtExceptionHandler

小结

  • ElasticsearchUncaughtExceptionHandler 实现了 Thread.UncaughtExceptionHandler 接口;uncaughtException 方法首先判断 throwable 是否是 Error 类型,是的话则执行 onFatalUncaught(logger.error),然后执行 halt 方法,不是则执行 onNonFatalUncaught(logger.warn)
  • halt 方法 AccessController.doPrivileged 来执行对应 status 的 PrivilegedHaltAction,该 action 执行的是 Runtime.getRuntime().halt(status)
  • Bootstrap 的 init 静态方法使用 Thread.setDefaultUncaughtExceptionHandler(new ElasticsearchUncaughtExceptionHandler()) 来给线程设置 defaultUncaughtExceptionHandler

doc

  • ElasticsearchUncaughtExceptionHandler
退出移动版