ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Envoy Fine-Grain Logger:细粒度日志级别控制与运行时动态更新实战指南

Envoy Fine-Grain Logger:细粒度日志级别控制与运行时动态更新实战指南 Envoy Fine-Grain Logger细粒度日志级别控制与运行时动态更新实战指南【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy导读Fine-Grain Logger 是 Envoy 提供的一种比传统 Logger 粒度更细的日志方案它支持按源文件级别精确控制日志级别并可通过管理接口admin page在运行时动态调整全程无需开发者显式指定日志组件也无需重新编译或重启进程。阅读本文你将掌握如何用FINE_GRAIN_LOG系列宏在代码中输出细粒度日志、如何通过--enable-fine-grain-logging命令行选项一键启用、如何借助 admin 的/logging端点配合 glob 通配符按文件/路径批量调整日志级别以及该机制在 Envoy 源码中的完整实现原理。说明Fine-Grain Logger 最初命名为Fancy Logger后因其名称语义不透明、无法体现细粒度日志控制能力而更名为现在的名字见 source/docs/fine_grain_log.md。一、概述Fine-Grain Logger 与传统 Logger 的差异Envoy 传统 Logger 采用组件component粒度控制日志级别开发者需要在类中显式继承Envoy::Logger::Loggable并声明日志组件。Fine-Grain Logger 则完全不同文件级粒度以源文件__FILE__为最小控制单元日志级别按文件设置零显式声明完全自动化开发者不需要显式指定日志组件框架通过宏自动获取调用点所在文件可扩展性文件级控制可以轻松扩展为函数级function或行级line控制只需在键key中追加更细的维度运行时更新通过 admin 接口实时查看和修改日志级别无需重启性能相当文档明确指出其速度与 Envoy 原生 Logger 相当it has a comparable speed as Envoys logger这一点在仓库的 benchmark 测试 test/common/common/fine_grain_logger_benchmark.cc 和 test/common/common/logger_speed_test.cc 中有对应验证。二、基本使用Fine-Grain Logger 宏2.1 最简用法FINE_GRAIN_LOG最基本的用法是显式调用FINE_GRAIN_LOG宏第一个参数是日志级别后续参数与 fmt 格式化参数一致FINE_GRAIN_LOG(info, Hello world! Heres a line of fine-grain log!); FINE_GRAIN_LOG(error, FineGrainLog Error! Heres the second message!);当消息级别达到或超过该文件当前设置的级别时宏会打印消息且输出中自动携带文件名与行号格式如下[2020-07-29 22:27:02.594][15][error][test/common/common/log_macros_test.cc:149] FineGrainLog Error! Heres the second message!从输出可以看到四个字段依次为时间戳、线程 ID、日志级别、源文件路径:行号这正是 Fine-Grain Logger 默认日志格式[%Y-%m-%d %T.%e][%t][%l] [%g:%#] %v的体现其中%g为文件名、%#为行号。在源码层面FINE_GRAIN_LOG(LEVEL, ...)最终会以spdlog::source_loc{__FILE__, __LINE__, __func__}记录调用位置并通过ENVOY_LOG_COMP_LEVEL(*local_flogger, LEVEL)做级别预判后写入日志见 source/common/common/fine_grain_logger.h。2.2 携带连接与流信息的宏在网络代理场景中日志往往需要附带连接 ID、流 ID 以便串联排查。Fine-Grain Logger 提供了两个便捷宏内部会自动拼接[C{}]连接与[C{}][S{}]连接流前缀NiceMockNetwork::MockConnection connection_; NiceMockHttp::MockStreamDecoderFilterCallbacks stream_; FINE_GRAIN_CONN_LOG(warn, Fake info {} of connection, connection_, 1); FINE_GRAIN_STREAM_LOG(warn, Fake warning {} of stream, stream_, 1);其实现本质是转发到带空分组名的FINE_GRAIN_GROUP_LOG见 source/common/common/fine_grain_logger.h#define FINE_GRAIN_CONN_LOG(LEVEL, FORMAT, CONNECTION, ...) \ FINE_GRAIN_GROUP_LOG(LEVEL, , [C{}] FORMAT, (CONNECTION).id(), ##__VA_ARGS__) #define FINE_GRAIN_STREAM_LOG(LEVEL, FORMAT, STREAM, ...) \ FINE_GRAIN_GROUP_LOG(LEVEL, , [C{}][S{}] FORMAT, \ (STREAM).connection() ? (STREAM).connection()-id() : 0, \ (STREAM).streamId(), ##__VA_ARGS__)2.3 冲刷日志FINE_GRAIN_FLUSH_LOG需要立即将日志落盘时调用FINE_GRAIN_FLUSH_LOG()即可。其底层通过getFineGrainLogContext().getFineGrainLogEntryForFlush(__FILE__, NAME)找到当前文件的 logger 并执行flush()见 source/common/common/fine_grain_logger.h。若当前文件尚未初始化过 loggergetFineGrainLogEntryForFlush会返回空指针并安全跳过。三、启用 Fine-Grain Logger命令行选项与宏替换机制3.1--enable-fine-grain-logging命令行选项Envoy 提供了命令行选项--enable-fine-grain-logging来启用 Fine-Grain Logger。该选项在 source/server/options_impl.cc 中以 TCLAPSwitchArg定义TCLAP::SwitchArg enable_fine_grain_logging( , enable-fine-grain-logging, Logger mode: enable file level log control (Fine-Grain Logger) or not, cmd, false);启用后Envoy 绝大多数日志宏会被替换为对应的 Fine-Grain Logger 宏具体对应关系为Envoy 原生宏被替换为ENVOY_LOGFINE_GRAIN_LOG按调用文件ENVOY_FLUSH_LOGFINE_GRAIN_FLUSH_LOGENVOY_CONN_LOGFINE_GRAIN_CONN_LOGENVOY_STREAM_LOGFINE_GRAIN_STREAM_LOG在宏实现层面见 source/common/common/logger.hENVOY_LOG_TO_LOGGER在 Fine-Grain 模式下会走FINE_GRAIN_GROUP_LOG(LEVEL, LOGGER.name(), ...)分支而ENVOY_LOG被定义为ENVOY_LOG_TO_LOGGER(ENVOY_LOGGER(), ...)因此传统代码无需改动即可自动获得细粒度能力。3.2 启用后的默认行为启用 Fine-Grain Logger 后默认日志格式变为[%Y-%m-%d %T.%e][%t][%l] [%g:%#] %v相比 Envoy 默认格式省略了 logger 名称字段因为 logger 名称与文件名相同重复展示没有意义默认日志级别为info除非用户显式通过日志上下文logging context指定该默认格式常量在 source/common/common/fine_grain_logger.h 中定义为kDefaultFineGrainLogFormat。3.3 不被替换的宏兼容性边界并非所有宏都会被替换。以下宏在 Fine-Grain 模式下保持不变用于显式指定 logger 的高级场景GET_MISC_LOGGER, ENVOY_LOG_MISC, ENVOY_LOGGER, ENVOY_LOG_TO_LOGGER, ENVOY_CONN_LOG_TO_LOGGER, ENVOY_STREAM_LOG_TO_LOGGER例如ENVOY_LOG_TO_LOGGER(ENVOY_LOGGER(), LEVEL, ...)等价于 Envoy 模式下的ENVOY_LOG。这一点在文档中特别强调见 source/docs/fine_grain_log.md。3.4 未启用时的行为与手动启用如果未启用--enable-fine-grain-loggingEnvoy 继续使用传统 LoggerFINE_GRAIN_LOG等基本宏仍可单独使用ENVOY_LOG主体行为保持原样一个限制此模式下 admin 页面的日志级别更新默认不可用因为系统会检测到 Envoy 模式——Envoy 模式的设计初衷仅为向后兼容back compatible。如需在未使用命令行选项的情况下手动启用开发者可调用Logger::Context::enableFineGrainLogger()。该方法会更新当前 logging context 的enable_fine_grain_logging_标志并在log_format_为 Envoy 默认格式时切换到kDefaultFineGrainLogFormat随后调用setDefaultFineGrainLogLevelFormat把新级别/格式应用到所有已注册 logger见 source/common/common/logger.cc。另外需要注意--enable-fine-grain-logging与--component-log-level存在互斥约束同时指定会报错见 source/server/options_impl.cc。四、运行时动态更新admin/logging端点Fine-Grain Logger 的运行时更新依赖 admin 接口且必须处于 Fine-Grain 模式即已启用该功能才能使用。与传统 Envoy Logger 相同admin/logging提供以下四类功能操作说明POST /logging列出所有活跃 logger即文件路径及其日志级别POST /logging?file_pathlevel按完整文件路径修改单个文件的日志级别POST /logging?pathsfile_path1:level1,file_path2:level2...一次性批量修改多个文件路径的日志级别POST /logging?levellevel修改所有 logger 的默认级别例如POST /logging?pathssource/common/event/dispatcher_impl.cc:debug会把dispatcher_impl.cc的级别改为 debug。4.1 glob 通配符匹配规则Envoy admin/logging在 Fine-Grain Logger 更新时支持文件 basename去掉路径后缀的主文件名、glob*与?通配符匹配。通配符匹配的语义由FineGrainLogContext::safeFileNameMatch实现见 source/common/common/fine_grain_logger.cc仅支持*匹配任意长度含/与?匹配单个字符不支持方括号表达式[...]包含/的 pattern 匹配完整路径不包含/的 pattern 匹配主文件名stem basename即去掉目录前缀与最后一个扩展名多个 pattern按顺序匹配第一个命中生效exit-early 语义见getLogLevel中return info.log_level的写法未命中任何 pattern 的 logger 使用默认 verbosity 级别。4.2 完整示例假设当前活跃 logger 如下假设 Envoy 当前活跃的 logger0 表示 trace 级别source/server/admin/admin_filter.cc: 0 source/common/event/dispatcher_impl.cc: 0 source/common/network/tcp_listener_impl.cc: 0 source/common/network/udp_listener_impl.cc: 0以下六个操作可以完整演示 glob 匹配规则示例来自 source/docs/fine_grain_log.md1. 精确路径更新POST /logging?pathssource/common/event/dispatcher_impl.cc:debug结果source/common/event/dispatcher_impl.cc级别变为 debug其余 logger 保持默认级别。2. 文件 basename 更新POST /logging?admin_filterinfo结果source/server/admin/admin_filter.cc级别变为 info其他未匹配的 logger 保持默认级别示例中为 trace。3. 带/的 glob 匹配完整路径前缀POST /logging?pathssource/common*:warning结果source/common/event/dispatcher_impl.cc、source/common/network/tcp_listener_impl.cc变为 warning未匹配的 logger 会被重置为默认级别例如admin_filter.cc即使上一步刚更新为 info也会被恢复为默认 trace——因为每次更新会重建匹配集合。4.?通配单个字符POST /logging?paths???_listener_impl:info结果source/common/network/tcp_listener_impl.cc、source/common/network/udp_listener_impl.cc均变为 info???匹配tcp/udp。5. 首个匹配优先POST /logging?paths???_listener_impl:info,tcp_listener_impl:warning结果source/common/network/tcp_listener_impl.cc级别为info因为???_listener_impl在前第一个匹配生效。6. 修改全部默认级别POST /logging?levelinfo结果默认 verbosity 级别改为 info后续更新中所有未匹配的 logger 都将使用该默认级别。4.3 服务端处理逻辑/logging端点的处理实现在 source/server/admin/logs_handler.cchandlerLogging在列出日志时按模式区分非 Fine-Grain 模式遍历Logger::Registry::loggers()Fine-Grain 模式调用getFineGrainLogContext().listFineGrainLoggers()logs_handler.ccchangeLogLevel解析参数level走全局级别变更paths走批量name:level对group参数仅在 Fine-Grain 模式下允许否则返回group parameter requires fine-grain logging to be enabledlogs_handler.cc所有paths/group更新最终汇聚到glob_levels向量再交给getFineGrainLogContext().updateVerbositySetting(glob_levels)一次性生效logs_handler.cc。五、实现原理剖析Fine-Grain Logger 在架构上分为两部分见 source/docs/fine_grain_log.mdCore 核心部分不依赖显式继承Envoy::Logger::Loggable的文件级 loggerHook 钩子部分控制接口即命令行选项与 admin 页面。5.1 核心数据结构FineGrainLogContext核心实现位于class FineGrainLogContext它是一个单例类通过MUTABLE_CONSTRUCT_ON_FIRST_USE构造见 source/common/common/fine_grain_logger.cc主要成员见 source/common/common/fine_grain_logger.h成员类型作用fine_grain_log_map_absl::flat_hash_mapstring, SpdLoggerSharedPtr存储文件名(key), logger指针映射logger_keys_absl::flat_hash_mapstring, pairstring,string记录每个 key 对应的{file, name}元数据用于重建日志组件名verbosity_update_info_vectorVerbosityLogUpdateInfo存储pattern, level, 是否匹配分组的更新序列覆盖默认级别verbosity_default_level_spdlog::level::level_enum默认 verbosity 级别初始为infofine_grain_log_lock_absl::Mutex保护上述全局结构的读写锁其中VerbosityLogUpdateInfo记录三个关键判定字段update_is_pathpattern 是否含/决定匹配完整路径还是 basename、match_group_only是否仅匹配 logger 分组名、update_patternglob 表达式。5.2FINE_GRAIN_LOG的三条调用路径当FINE_GRAIN_LOG被调用时FINE_GRAIN_LOGGER(name)宏见 source/common/common/fine_grain_logger.h使用函数内静态std::atomicspdlog::logger*缓存局部 logger 指针根据首次调用情况分为三条路径慢路径Slow path如果某文件内首次调用FINE_GRAIN_LOG调用FineGrainLogContext::initFineGrainLogger(key, local_logger_ptr)在全局创建新 logger并把key, global_logger_ptr存入fine_grain_log_map_此后该文件内局部指针被更新且永不再变中路径Medium path如果调用点是文件内首次、但该文件已在全局注册initFineGrainLogger依然被调用但局部指针被快速设置为全局 logger因为全局已有该文件名的记录无需重复创建 spdlog logger快路径Fast path调用点已初始化过后日志直接用缓存的局部 logger 指针输出不再加锁、不再查表这是性能与 Envoy 原生 Logger 相当的关键。在initFineGrainLogger内部fine_grain_logger.cckey 的构造规则是无分组时 key 即文件名file有分组时 key 为file:name。如果 key 已存在于 map 则复用已有 logger否则调用createLogger新建——新 logger 的级别由getLogLevel(key)决定受verbosity_update_info_与默认级别影响格式使用Context::getFineGrainLogFormat()并设置flush_on(critical)critical 及以上立即冲刷。5.3 级别匹配算法getLogLevelgetLogLevel(key)fine_grain_logger.cc是按文件找级别的核心若verbosity_update_info_为空直接返回默认级别解析 key 得到{file, name}计算 file 的 basename 与 stem basename去掉最后一个扩展名如tcp_listener_impl.cc→tcp_listener_impl依序遍历verbosity_update_info_match_group_only为真时仅当name非空且与 pattern 相等才命中pattern 含/update_is_path时对完整路径做safeFileNameMatchpattern 不含/时依次对stem basename做 glob 匹配、再对name做精确比较第一个命中即返回对应级别首个匹配优先全部未命中则返回默认级别。这也解释了第 4.2 节示例 5 的行为???_listener_impl先匹配成功tcp_listener_impl:warning永远不会被检查到。5.4 Hook 部分命令行与 admin 的接线命令行侧--enable-fine-grain-logging解析后Logger::Context构造时接收enable_fine_grain_logging参数见 source/common/common/logger.cc并在Context::activate()中logger.cc将log_level_、log_format_同步到fine_grain_default_level_、fine_grain_log_format_若启用 Fine-Grain 且格式为 Envoy 默认格式则切换为kDefaultFineGrainLogFormat调用getFineGrainLogContext().setDefaultFineGrainLogLevelFormat(level, format)把新级别/格式应用到全部已注册 logger并同步verbosity_default_level_。admin 侧LogsHandler通过四个FineGrainLogContext方法对接listFineGrainLoggers()→ 展示所有 Fine-Grain Logger 及其级别setFineGrainLogger(name, level)→ 按 key 设置单个 logger 级别未找到返回 falsesetAllFineGrainLoggers(level)→ 设置全部 logger 级别同时清空verbosity_update_info_updateVerbositySetting(glob_levels)→ 批量设置 glob 匹配的 logger先校验 level 是否在[0, 6]范围内越界则跳过并打印告警见 fine_grain_logger.cc。注意 spdlog 的 level 枚举范围为[0, 6]对应trace/debug/info/warn/err/critical/offkLogLevelMin0、kLogLevelMax6见 fine_grain_logger.h文档示例中的0即 trace 级别。六、测试与验证仓库提供了完整的测试与基准覆盖可用于验证本文所述行为test/common/common/log_macros_test.cc验证FINE_GRAIN_LOG、FINE_GRAIN_CONN_LOG、FINE_GRAIN_STREAM_LOG等宏的输出格式与级别过滤行为本文开头的输出示例即来源于此test/common/common/log_verbosity_update_test.cc验证updateVerbositySetting、glob 匹配、默认级别覆盖等运行时更新语义test/common/common/fine_grain_logger_benchmark.ccFine-Grain Logger 的性能基准配合 test/common/common/logger_speed_test.cc 佐证其与原生 Logger 性能相当的设计目标source/common/common/fine_grain_logger.h 还提供了removeFineGrainLogEntryForTest用于测试隔离。七、总结Fine-Grain Logger 通过文件名即日志组件的设计把 Envoy 日志控制粒度从组件级细化到文件级并借助FineGrainLogContext单例 函数内静态原子指针缓存实现了慢/中/快三条路径首次调用建 logger、文件内复用、热路径零开销。配合--enable-fine-grain-logging命令行开关与 admin/logging端点的paths、level、group参数及 glob 通配符运维人员可以在不重启 Envoy 的前提下按文件、按路径前缀、按主文件名批量实时调整日志级别且级别设置首个匹配优先、未匹配回退默认的语义清晰可预期。对于需要精细排障与动态观测的代理运维场景这是一套开箱即用且性能友好的解决方案。【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表