你真的知道如何查看 Elasticsearch 的 Debug 日志吗?!

你真的知道如何查看 Elasticsearch 的 Debug 日志吗?!

当我们遇到问题或者需要深入了解 Elasticsearch 的运行机制时,调整日志等级( logging level )到更详细的级别,比如 DEBUG TRACE ,会是一个有效且必须要掌握的方法。


Elasticsearch 提供了如下的接口来支持动态变更 logging level,logger 后面是 package name 或者 class name。


PUT _cluster/settings
  "persistent": {
    "logger": {
      "org.elasticsearch.action": "DEBUG"


当然,你也可以去修改配置目录下面的 log4j2.properties,然后重启节点,但这种方法太过笨重,建议你不要用。


如果后续想要调整回默认设置,操作也简单,如下所示:


PUT _cluster/settings
  "persistent": {
    "logger": {
      "org.elasticsearch.action": null


上面的示例只是指定了一个 logger,当然也可以在一次请求中设定多个 logger,如下所示:


PUT _cluster/settings
  "persistent": {
    "logger": {
      "_root": "INFO",
      "org.elasticsearch.action": "DEBUG",
      "org.elasticsearch.action.admin.cluster.health": "TRACE"


上面的设定中,调用者的意图可能如下:


  1. root 的日志等级(默认所有 logger 的日志级别)设定为 INFO,虽然 log4j2.properties 中已经设定过了,保险起见,这里再指定一次。

  2. 设定 org.elasticsearch.action 这个 package 下所有 logger 的日志级别都为 DEBUG,需要查看下 transport action 的执行日志。

  3. 设定 org.elasticsearch.action.admin.cluster.health 这个 package 下所有 logger 的日志级别都为 TRACE,需要查看 Cluster Health 执行的更多日志。


但实际去运行时,Elasticsearch 并没有按照预期的结果去执行,没有相关 DEBUG TRACE 级别的日志输出。这里直接给出原因和解决方案。


原因是 elasticsearch 在设定 logging level 时,会 优先采用 _root parent logger 的设定,这里和 log4j2.properties 中的设定有所差异。


上面的调用,最终结果是采用 _root 的设定,所有 logger 都是 INFO ,其他的设定都无效了。


解决方案如下,去除 _root 设定和 parent logger 的设定。


PUT _cluster/settings
  "persistent": {
    "logger": {
      "_root": null,
      "org.elasticsearch.action": null,
      "org.elasticsearch.action.admin.cluster.health": "TRACE"


下面就是 源码分析 了,感兴趣的可以继续看下去~


源码分析


相关实现逻辑在 ClusterSetting.LoggingSettingUpdater 里面,这里简单给下定位的思路,感兴趣的同学可以自己去翻下源码。


  1. rest 请求的入口是 RestClusterUpdateSettingsAction ,这里会转发请求到 master 节点

  2. master 处理的入口是 TransportClusterUpdateSettingsAction ,这里会去 update Cluster Setting,关键词为 updater.updateSettings

  3. 在 updateSettings的时候会调用所有的 ClusterSettingUpdater,Logging 就是其中之一。


这里 apply setting 的代码如下:


for (String key : value.keySet()) {
    assert loggerPredicate.test(key);
    String component = key.substring("logger.".length());
    if ("level".equals(component)) {
        continue;
    if ("_root".equals(component)) {
        final String rootLevel = value.get(key);
        if (rootLevel == null) {
            Loggers.setLevel(LogManager.getRootLogger(), Loggers.LOG_DEFAULT_LEVEL_SETTING.get(settings));
        } else {
            Loggers.setLevel(LogManager.getRootLogger(), rootLevel);
    } else {
        Loggers.setLevel(LogManager.getLogger(component), value.get(key));


浅显易懂,不废话,而且这里的逻辑看起来很正常,那么继续来看下 Loggers.setLevel代码。


public static void setLevel(Logger logger, Level level) {
    if (!LogManager.ROOT_LOGGER_NAME.equals(logger.getName())) {
        Configurator.setLevel(logger.getName(), level);
    } else {
        final LoggerContext ctx = LoggerContext.getContext(false);
        final Configuration config = ctx.getConfiguration();
        final LoggerConfig loggerConfig = config.getLoggerConfig(logger.getName());
        loggerConfig.setLevel(level);
        ctx.updateLoggers();
    // we have to descend the hierarchy
    final LoggerContext ctx = LoggerContext.getContext(false);
    for (final LoggerConfig loggerConfig : ctx.getConfiguration().getLoggers().values()) {