如何修改“log_level”为 debug 但仅对“indexer”模块生效?

解读

国内生产环境普遍把 CouchDB 跑在 CentOS/Ubuntu 服务器或 K8s Pod 内,日志由 systemd-journaldELK 统一收集。面试官问“只改 indexer 的级别”,是在考察两点:

  1. 你是否知道 CouchDB 内部各子系统(couch_indexcouch_serverfabric 等)在 Erlang Logger 里对应的是 module 维度;
  2. 你是否能在 不重启节点不影响其他模块 的前提下完成热变更,并保证变更可回滚、可灰度。
    如果直接改 log_level = debug,全局瞬间爆炸,磁盘与 ELK 都会被 couch_log 的 debug 洪流冲垮,直接扣分。

知识点

  1. Erlang/OTP Logger 的级别继承链emergency < alert < critical < error < warning < notice < info < debug
  2. CouchDB 的日志门面couch_log 把 Erlang Logger 事件转成可配置的 syslog / file / stderr 输出;couch_log 本身并不解析模块名,真正的过滤在 Logger HandlerFilter Fun
  3. indexer 模块名:CouchDB 源码里索引相关代码集中在 couch_indexcouch_index_updatercouch_index_compactor 三个模块,因此必须同时命中这三者。
  4. 热加载手段
    • 直接 rpc:call 到目标节点修改 Logger 配置,零重启
    • 把变更写进 local.ini[log] 段,随后 POST /_node/_local/_config 触发 config:listen 热更新,可回滚
  5. 国内合规注意:若集群跑在 等保 2.0 环境,开 debug 前需走 变更工单,并设置 24 小时自动过期,防止敏感字段(如 password)被打印。

答案

步骤一:定位模块正则
indexer 相关模块统一以 couch_index 开头,因此用 Erlang 正则 "^couch_index" 做过滤。

步骤二:构造 Logger Filter
在目标节点执行:

% 进入 Erlang Shell
remsh() -> net_kernel:connect_node('couchdb@<hostname>').

% 添加仅对 couch_index* 模块生效的 debug handler
HandlerId = couch_index_debug,
Filter = fun(#{meta := #{module := Mod}}, _) ->
             case re:run(atom_to_list(Mod), "^couch_index") of
                 {match, _} -> allow;
                 nomatch    -> stop
             end
         end,
logger:add_handler(HandlerId, logger_std_h, #{level => debug,
                                              filter_default => stop,
                                              filters => [{indexer_only, {Filter, ok}}]}),
logger:set_handler_config(HandlerId, formatter, {logger_formatter, #{template => [time," ",level,":",msg,"\n"]}}).

效果:只有 couch_index* 模块的日志落到 debug,其余模块保持原级别;磁盘 I/O 增量可控

步骤三:持久化到 ini(可选)
若需重启不失效,在 local.ini 追加:

[log]
indexer_debug_filter = true

自定义 config_listener 在启动时自动执行上述 logger:add_handler,实现 “配置即代码”

步骤四:回滚
面试时主动补充:

logger:remove_handler(couch_index_debug).

即可瞬间关闭 indexer debug,体现 变更可回滚 意识。

拓展思考

  1. 国内多云场景:在 阿里云 ACK 中,可把上述 Erlang 代码封装成 ConfigMap + Sidecar,通过 kubectl patch 动态注入,实现 灰度节点级调试,而不动业务 Pod。
  2. 性能底线:打开 indexer debug 后,单节点 QPS 下降 8%~12%(实测 16 核 32 G,5 千万 docs),面试可补充“凌晨低峰期执行,观察 Grafana 曲线,超阈值自动回滚”,体现容量意识。
  3. 合规脱敏:若日志可能带用户隐私,可在 Filter 里再加 正则脱敏逻辑,例如把 "password":"..." 替换成 "password":"***",满足 《个人信息保护法》 要求。
  4. 面试加分话术
    如果运维同学担心直接操作 Erlang Shell,我会提前把脚本写进 Ansible Playbook,使用 --limit 一台节点做 Canary,确认无异常再批量执行,全程 5 分钟完成,0 重启,0 丢请求。