当前位置: 首页 > news >正文

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

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

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 hierarchyfinal LoggerContext ctx = LoggerContext.getContext(false);for (final LoggerConfig loggerConfig : ctx.getConfiguration().getLoggers().values()) {if (LogManager.ROOT_LOGGER_NAME.equals(logger.getName()) || loggerConfig.getName().startsWith(logger.getName() + ".")) {Configurator.setLevel(loggerConfig.getName(), level);}}
}

最后的处理逻辑会在每个 logger 设定完成后,去重新刷一遍现有的 logger,应用 root 或者 parent logger 的设定

顺着代码的修改记录,找到了当初的修改 PR 如下:

[https://github.com/elastic/el...]()

其中也描述了修改的原因:

Today when setting the logging level via the command-line or an API
call, the expectation is that the logging level should trickle down the
hiearchy to descendant loggers. However, this is not necessarily the
case. For example, if loggers x and x.y are already configured then
setting the logging level on x will not descend to x.y. This is because
the logging config for x.y has already been forked from the logging
config for x. Therefore, we must explicitly descend the hierarchy when
setting the logging level and that is what this commit does.

从这段描述看,当时要解决的问题是 x.y 没有继承 x logging level 的问题,所以加了这段显示继承的逻辑。

虽然这解决了继承的问题,但其行为本身与 log4j2.properties 中 logger 的修改逻辑就不一致了,难免带来困扰。

但考虑到这个配置是一个专家级别的配置,很少用户会使用,自己心里明白正确的使用方法就好了^_^

相关文章:

  • 后面的输入框与前面的联动,输入框只能输入正数(不用正则)
  • Gazebo的模型下载。
  • 面试题16.15.珠玑妙算
  • 【大数据分析与挖掘技术】概述
  • 智能助手的巅峰对决:ChatGPT对阵文心一言
  • C++浮点数比较
  • MySQL经典面试题
  • SQL执行时间过长如何优化
  • Spring MVC学习之——上传文件
  • C# .NET读取Excel文件并将数据导出到DataTable、数据库及文本
  • 什么是CSS Sprite,以及如何在页面或网站中使用它
  • 【mfc/VS2022】绘图工具设计-绘制基本图元2
  • 常用中间件漏洞
  • 笨蛋学设计模式行为型模式-观察者模式【14】
  • “深入理解网络科学与自定义网络构建“
  • 【5+】跨webview多页面 触发事件(二)
  • 【刷算法】从上往下打印二叉树
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • 4月23日世界读书日 网络营销论坛推荐《正在爆发的营销革命》
  • CSS 三角实现
  • Druid 在有赞的实践
  • el-input获取焦点 input输入框为空时高亮 el-input值非法时
  • Meteor的表单提交:Form
  • Netty 框架总结「ChannelHandler 及 EventLoop」
  • PV统计优化设计
  • Python中eval与exec的使用及区别
  • sessionStorage和localStorage
  • 猫头鹰的深夜翻译:Java 2D Graphics, 简单的仿射变换
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 学习Vue.js的五个小例子
  • 一个6年java程序员的工作感悟,写给还在迷茫的你
  • 用 Swift 编写面向协议的视图
  • ​Java并发新构件之Exchanger
  • ​Linux Ubuntu环境下使用docker构建spark运行环境(超级详细)
  • #pragma once
  • #WEB前端(HTML属性)
  • (11)工业界推荐系统-小红书推荐场景及内部实践【粗排三塔模型】
  • (52)只出现一次的数字III
  • (C#)获取字符编码的类
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (转)jQuery 基础
  • .Net 6.0 处理跨域的方式
  • .NET Conf 2023 回顾 – 庆祝社区、创新和 .NET 8 的发布
  • .Net Core webapi RestFul 统一接口数据返回格式
  • .NET Core实战项目之CMS 第十二章 开发篇-Dapper封装CURD及仓储代码生成器实现
  • .Net Core与存储过程(一)
  • .NET Core中Emit的使用
  • .net专家(高海东的专栏)
  • [Asp.net MVC]Asp.net MVC5系列——Razor语法
  • [bug总结]: Feign调用GET请求找不到请求体实体类
  • [C#]winform部署yolov9的onnx模型
  • [ERROR] Plugin 'InnoDB' init function returned error
  • [Everyday Mathematics]20150130
  • [HITCON 2017]SSRFme perl语言的 GET open file 造成rce
  • [J2ME]如何替换Google Map静态地图自带的Marker