跳转至

SOP —— 日志级别解析与排查视图(14 台主机)

变更日期:2026-10-01 | 工程:monitoring-p2-20261001 | 类型:功能新增 + 采集方式变更(C 类) 关联:docs/SOP-kibana-domain-view.md(分域视图本体)、docs/RUNBOOK.md §4.5、backup/kibana-domain-view-20261001/README.md

一、背景

EFK 上线后日志只到「可查性 L1」:只有 Filebeat 自动加的元数据(host.name / cluster / log_source), 日志内容一个字段都没解析——实测全库 280 万条 log.level 100% 为 null。 结果是只能看"日志量",无法回答「谁在报错、错了多少、错在哪」。

本轮目标:让集群外 14 台的系统日志产出 ECS 标准字段,使视图支持 按主机 / Pod 定位 → 按级别(error/warn)过滤 → 看明细 这条排查主路径。

二、目标

  • 集群外 14 台的系统日志(原 /var/log/messages、/var/log/secure、/var/log/cron)入库后带 log.level(error/warning/informational…)、log.syslog.appname、host.hostname
  • Kibana「日志分域视图」新增:主机 / K8s Pod / 级别 / 集群域 / 日志类别 五个筛选控件, 新增「日志级别趋势」与「异常来源 · 按主机」两个面板,明细表补 log.level 列

三、前置条件

  • 目标主机为 RHEL/Rocky 9,journalctl 可用,filebeat 以 root 运行(systemctl show filebeat -p User 为空即 root)
  • 主机 Filebeat ≥ 8.x(本环境 8.19.0),支持 journald input 与 script processor
  • 与本轮相关的既有能力:工程幂等下发(HostFilebeat.converge_all)、Kibana 声明式下发(main.py kibana)

四、影响范围

项 说明
目标 集群外 14 台主机的 /etc/filebeat/filebeat.yml + filebeat.service
是否停服 否;仅重启 Filebeat,采集中断为秒级
采集面变化 messages / secure / cron 三处文件采集合并为 journald 一路(journald 是其上游,避免重复入库)
数据面 新数据带 log.level;老数据不回溯(仍是 null)
K8s 侧 本次未改(容器日志格式因应用而异,另行处理)

五、分步操作

步骤 1:为什么必须先换数据源(关键发现)

先用 syslog processor 解析 /var/log/messages 行——解析成功但拿不到级别。实测输出:

"log": { "syslog": { "appname": "sshd", "procid": "99", "hostname": "host01" } }

原因:rsyslog 落盘时已剥离 syslog priority,/var/log/messages 的行里根本没有 <PRI> 前缀, processor 无从推断级别。行业里能拿到级别的路径是 journald(保留 PRIORITY 字段)。

验证 journald 可用性与字段:

ssh root@192.168.31.16 'journalctl -n 1 -o json --no-pager | head -c 400; systemctl show filebeat -p User'

预期:JSON 里含 SYSLOG_FACILITY、_HOSTNAME 等;User= 为空(root 运行)。

步骤 2:改采集模板

改动文件:artifacts/filebeat-host.yml.tpl

  • 删除 host-syslog、host-secure、host-cron 三条 filestream input
  • 新增 host-journald(type: journald),用 script processor 把 log.syslog.priority 映射为 log.level, 并按 log.syslog.facility.code == 10(authpriv)区分 log_source: auth,其余为 syslog
  - type: journald
    id: host-journald
    enabled: true
    seek: tail
    fields:
      cluster: __CLUSTER__
      role: __ROLE__
      host_name: __HOST_NAME__
    fields_under_root: true
    processors:
      - script:
          lang: javascript
          source: >
            function process(event) {
              var p = event.Get("log.syslog.priority");
              if (p === null || p === undefined) return;
              var names = ["emergency", "alert", "critical", "error",
                           "warning", "notice", "informational", "debug"];
              var i = parseInt(p, 10);
              if (!isNaN(i) && i >= 0 && i < 8) { event.Put("log.level", names[i]); }
              var f = event.Get("log.syslog.facility.code");
              event.Put("log_source", parseInt(f, 10) === 10 ? "auth" : "syslog");
            }

坑(每一条都实测踩过):

  1. seek: tail 不可省。不加时 journald input 会重放整个 journal(实测一次读出 23196 条历史), 与既有数据大面积重复。
  2. cron 必须一起删。journald 已含 CROND 标识符的条目,若保留 host-cron 的 filestream,cron 日志会双份入库。
  3. audit / 领域日志 / flexlm / vnc 保持 filestream 不动——它们不是 syslog 通道,不在 journald 里。
  4. 验证配置语法与 processor 行为时:filebeat --once 对 filestream / journald 无效(不产生事件), 要用限时运行;且 filebeat 自身日志与 output.console 都在 stdout,必须 2>/dev/null 分离。

步骤 3:单台先行验证(不碰生产配置)

把渲染后的配置推到一台机器上做隔离 stub 测试:

# 语法
filebeat test config -c /tmp/fe-test.yml          # 预期:Config OK
# processor 行为(限时 + 分离 stderr)
timeout 12 filebeat -c /tmp/fbtest/fb.yml -E path.data=/tmp/fbtest/data -e 2>/dev/null | head -40

预期事件里出现 "log": {"syslog": {"priority": 3}, "level": "error"}。

坑:path.data 写进 YAML 不生效(被运行中的主实例锁住,报 data path already locked), 必须用命令行 -E path.data=<独立目录> 覆盖。

步骤 4:批量下发

实现体:HostFilebeat.converge_all(幂等:SHA256 比对,一致即跳过;不一致才备份并覆盖 + 重启服务)。 等价底层操作(每台):

sha256sum /etc/filebeat/filebeat.yml                    # 与渲染结果比对
install -m 0600 -o root -g root <渲染后的配置> /etc/filebeat/filebeat.yml
systemctl restart filebeat && systemctl is-active filebeat

预期:actions: ["write-config", "start-service"],ok: true;复跑应零变更(actions: [])。

步骤 5:视图改造

在 kibana/saved_objects/ 内:

  • 新增 07-viz-level-trend.json(log.level 随时间的折线)
  • 新增 08-viz-error-by-host.json(KQL 过滤 log.level: (emergency or alert or critical or error or warning), 按 host.name 分布)
  • 更新 06-dashboard-domain-overview.json:面板增至 6 个,控件增至 5 个(主机 / Pod / 级别 / 集群域 / 类别)
  • 更新 05-search-recent-logs.json:明细列改为 log.level / log_source / host.name / log.syslog.appname / message

下发:

.venv/bin/python main.py kibana --dry-run     # 预览差异
.venv/bin/python main.py kibana               # 幂等下发

六、验证

  1. 字段出现: bash curl -s -u "elastic:$ELASTIC_PASSWORD" \ "http://localhost:9200/.ds-filebeat-host-*/_count?q=log.level:*" # 在 ES Pod 内执行 预期:> 0,且按 host.name 聚合能覆盖 14 台(实测首轮 135 条 / 14 台全覆盖)。
  2. 幂等:converge_all 复跑,14 台零变更。
  3. 视图:浏览器打开 https://kibana.rainchen.top/app/dashboards#/view/efk-dashboard-domain-overview 预期:6 个面板全部渲染(含「日志级别趋势」折线、「异常来源 · 按主机」柱状), 顶部 5 个控件可下拉;选择「主机 = X」+「级别 = error」后,各面板与明细同步过滤。
  4. 排查路径演练:选主机 → 选级别 error → 明细表看原始行,全程无需手写查询。

七、回滚

对象 回滚动作
模板 恢复 artifacts/filebeat-host.yml.tpl 到改动前(三个 filestream input 的版本,见 backup/kibana-domain-view-20261001/README.md 第二节记录的原文要点),重跑 converge_all
14 台主机 模板回滚后重跑 converge_all 即覆盖回旧配置并重启;单台手工回滚:cp /etc/filebeat/filebeat.yml.bak.<时间戳> /etc/filebeat/filebeat.yml && systemctl restart filebeat(工程在下发前自动留时间戳备份)
Kibana 视图 .venv/bin/python main.py kibana 重放声明即收敛;彻底回退见 SOP-kibana-domain-view.md 第七章
已入库的新数据 不回滚(log.level 为新增字段,保留无害)

八、风险说明

风险 缓解
journald 未持久化(本环境 /var/log/journal 不存在,仅内存) 采的是实时流,历史丢失不影响;Filebeat 用 registry 记 cursor,重启续采
采集面比原来宽(journald 含 kernel/dnf/NetworkManager 等) 属预期收益(更完整);量级实测约 12K 条/天/台,如需收缩可加 include_matches
log_source 取值变化:syslog 的含义由「messages+secure」变为「journald 非 authpriv 部分」 RUNBOOK §4.5 与视图描述已同步;旧数据仍为原值,按时间区分即可
script processor 语法错误会导致 Filebeat 启动失败 已按「单台 stub 验证 → 单台下发 → 批量」三步推进;systemctl is-active 与 ok 字段双重判定
K8s 容器日志的级别已在 §9 补齐;识别不出的格式(如 Go 标准 log)保持空值 属预期行为——不猜级别,避免误报

九、K8s 容器日志级别(同日第二阶段)

9.1 背景

K8s 容器日志(.ds-filebeat-8.19.0-*,近 30 万条,实测 log.level 计数为 0)同样没有级别。 与主机侧不同:容器 stdout 由 containerd 落盘,不经过 journald,只能在 Filebeat 侧按文本格式解析。

9.2 格式实况(实测取样)

来源 格式 样例
Grafana JSON,含 level {"time":"…","level":"INFO","msg":"Loading incluster config..."}
GitLab webservice JSON,无 level {"component":"gitlab","method":"GET","status":200,…}
calico klog I1001 13:51:37.490170 36 warnings.go:107] "Warning: unknown field"
Prometheus logfmt time=… level=error msg="…"
headlamp Go 标准 log 2026/10/01 13:51:33 traces export: …(无级别)

9.3 解析规则(四层,先匹配先返回)

  1. 已有 log.level → 跳过(幂等,不覆盖上游写的值)
  2. message 以 { 开头 → JSON.parse 后取 level / severity / loglevel / lvl
  3. klog 首字母 [IWEF]dddd → info / warning / error / critical
  4. logfmt level=xxx / lvl=xxx
  5. 行首级别标记 [ERROR] / ERROR: / WARN / INFO 等
  6. 都不匹配 → 不设(保持 null,不猜)

规范化:trace|debug|verbose→debug,info|notice→info,warn*→warning,err*→error, critical|fatal|panic|emerg|alert→critical。

兜底只匹配行首的级别标记,不做全文关键词匹配——后者会把正文里出现 "error" 一词的正常日志误判为错误。

9.4 实施

改动 manifests/12-filebeat-k8s.yaml:给 k8s-container-logs input 增加 script processor。

kubectl -n monitoring apply -f manifests/12-filebeat-k8s.yaml
kubectl -n monitoring rollout restart ds/filebeat          # ConfigMap 变更不会自动重建 Pod
kubectl -n monitoring rollout status ds/filebeat --timeout=180s

先验证再上集群:在任一主机上用隔离 stub 配置(8 条覆盖全部格式的样本)跑通 processor,确认 「能识别的识别出来、识别不出的保持原样」之后才 apply。

坑:

  1. YAML 的 > 折叠块会把多行 JS 压成一行 → JS 里不能用 // 注释(会把后续代码全部注释掉)。
  2. script processor 运行在 Filebeat 本地(goja 引擎),看不到 ES ingest pipeline 展开的字段, 因此必须自己 JSON.parse(message),不能依赖 logs@json-message 的结果。
  3. event.Get() 对不存在的字段返回 null,逐层判断需显式判空。

9.5 验证

# 在 ES Pod 内:近 10 分钟带级别的容器日志
curl -s -u "elastic:$ELASTIC_PASSWORD" -H "Content-Type: application/json" \
  "http://localhost:9200/.ds-filebeat-8.19.0-*/_search" \
  -d '{"size":0,"query":{"bool":{"must":[{"exists":{"field":"log.level"}},
       {"exists":{"field":"kubernetes.namespace"}},{"range":{"@timestamp":{"gte":"now-10m"}}}]}},
       "aggs":{"lvl":{"terms":{"field":"log.level","size":8}},"ns":{"terms":{"field":"kubernetes.namespace","size":10}}}}'

预期:total > 0;级别桶含 info / debug / error / warning(实测 428 条,覆盖 5 个命名空间)。 视图侧新增「异常来源 · 按 K8s Pod」面板,总计 7 个面板 + 5 个控件。

9.6 回滚

删除 manifests/12-filebeat-k8s.yaml 中 k8s-container-logs input 下的 processors 段, 重新 apply + rollout restart 即可;已入库数据里的 log.level 保留无害。

附录:交付物清单

文件 说明
artifacts/filebeat-host.yml.tpl 采集模板:journald + priority→level 映射(本轮核心改动)
kibana/saved_objects/07-viz-level-trend.json 新增:级别趋势
kibana/saved_objects/08-viz-error-by-host.json 新增:异常来源按主机
kibana/saved_objects/05、06-*.json 更新:明细列、面板与控件
backup/kibana-domain-view-20261001/README.md 模板改动清单与回退步骤

来源:/home/rain/Documents/k8s_ins/monitoring-p2-20261001/docs/SOP-log-level-parsing.md | 最后更新:2026-10-01 21:55(由 build/sync.py 同步,请勿在站点侧修改)