本文主要研究一下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