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),支持
journaldinput 与scriptprocessor - 与本轮相关的既有能力:工程幂等下发(
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三条filestreaminput - 新增
host-journald(type: journald),用scriptprocessor 把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");
}
坑(每一条都实测踩过):
seek: tail不可省。不加时 journald input 会重放整个 journal(实测一次读出 23196 条历史), 与既有数据大面积重复。- cron 必须一起删。
journald已含CROND标识符的条目,若保留host-cron的 filestream,cron 日志会双份入库。 audit/ 领域日志 / flexlm / vnc 保持 filestream 不动——它们不是 syslog 通道,不在 journald 里。- 验证配置语法与 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 # 幂等下发
六、验证¶
- 字段出现:
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 台全覆盖)。 - 幂等:
converge_all复跑,14 台零变更。 - 视图:浏览器打开
https://kibana.rainchen.top/app/dashboards#/view/efk-dashboard-domain-overview预期:6 个面板全部渲染(含「日志级别趋势」折线、「异常来源 · 按主机」柱状), 顶部 5 个控件可下拉;选择「主机 = X」+「级别 = error」后,各面板与明细同步过滤。 - 排查路径演练:选主机 → 选级别 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 解析规则(四层,先匹配先返回)¶
- 已有
log.level→ 跳过(幂等,不覆盖上游写的值) message以{开头 →JSON.parse后取level/severity/loglevel/lvl- klog 首字母
[IWEF]dddd→ info / warning / error / critical - logfmt
level=xxx/lvl=xxx - 行首级别标记
[ERROR]/ERROR:/WARN/INFO等 - 都不匹配 → 不设(保持 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。
坑:
- YAML 的
>折叠块会把多行 JS 压成一行 → JS 里不能用//注释(会把后续代码全部注释掉)。 scriptprocessor 运行在 Filebeat 本地(goja 引擎),看不到 ES ingest pipeline 展开的字段, 因此必须自己JSON.parse(message),不能依赖logs@json-message的结果。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同步,请勿在站点侧修改)