3、日志管理基础:结构化日志 vs 非结构化日志、日志级别、日志轮转与归档策略
日志这东西,说白了就是数据管线的黑匣子。没有它,你连管道在哪爆的都查不出来。我见过太多团队,线上出问题了第一反应是「重启试试」,结果重启完连根毛都捞不着。嗯,今天我们就来聊聊日志管理的基础三板斧。
3.1 结构化日志 vs 非结构化日志
先问个问题:你平时打日志是用 print() 还是用 JSON?
非结构化日志,就是那种纯文本拼接。比如:
2025-03-20 14:23:10 ERROR 连接数据库失败:timeout after 30s
这种日志人眼看着还行,但机器要解析就头疼了。你得写正则去抠「ERROR」「timeout」这些字段,而且不同开发写的格式还不一样。我在项目中遇到过,一个团队里有人写 [ERROR],有人写 ERROR:,还有人写 【错误】…… 那叫一个酸爽。
结构化日志就不一样了。它用固定的格式,比如 JSON:
{
"timestamp": "2025-03-20T14:23:10.123Z",
"level": "ERROR",
"logger": "com.data.pipeline.db",
"message": "连接数据库失败",
"reason": "timeout after 30s",
"duration_ms": 30000,
"pipeline_id": "pl_001"
}
你看,字段名、类型、层级都清清楚楚。日志收集工具(比如 Filebeat、Fluentd)可以直接解析,不需要写正则。查询的时候也方便,在 Kibana 里直接搜 level:ERROR AND duration_ms:>10000 就行。
我个人习惯,所有生产环境的日志都走结构化。开发阶段偶尔用用 print() 调试,但上线前必须改成结构化格式。你想想看,如果日志不能自动解析,那告警规则怎么写?监控面板怎么配?
| 对比维度 | 非结构化日志 | 结构化日志 |
|---|---|---|
| 可读性 | 人眼友好 | 需要格式化工具 |
| 机器解析 | 需要正则,易出错 | 直接解析,零配置 |
| 查询效率 | 全文检索,慢 | 字段索引,快 |
| 扩展性 | 加字段需改解析逻辑 | 加字段不影响下游 |
| 典型场景 | 本地调试、临时排查 | 生产监控、告警、分析 |
3.2 日志级别:DEBUG、INFO、WARN、ERROR
日志级别这东西,看着简单,但用好的不多。我见过有人把所有日志都打成 INFO,也见过有人把数据库连接失败打成 WARN。嗯,这里要注意,级别选错了,告警就废了。
标准级别从低到高:
- DEBUG:开发调试用。比如变量值、函数入口出口。生产环境默认关闭,否则日志量能把你磁盘撑爆。
- INFO:正常业务流程的关键节点。比如「任务启动」「数据写入完成」「处理记录数:1000」。这些日志用来追踪流水线运行状态。
- WARN:不影响当前运行,但值得关注。比如「重试第 2 次」「磁盘使用率 80%」。我曾经因为忽略 WARN 日志,导致半夜磁盘写满,整个流水线挂了 40 分钟。
- ERROR:功能不可用,需要立即处理。比如「数据库连接失败」「数据校验不通过」。ERROR 日志应该直接触发告警。
还有一个容易被忽略的点:日志级别的动态调整。生产环境默认只开 INFO 及以上,但排查问题时可以临时把某个模块的级别降到 DEBUG。比如:
# 在配置中心动态修改
logger.com.data.pipeline.transform.level = DEBUG
排查完再改回去。这样既不影响性能,又能拿到细粒度信息。我个人习惯把这个能力集成到告警系统的「一键诊断」功能里。
3.3 日志轮转与归档策略
日志如果不轮转,后果很严重。我见过一个案例:某团队把日志打到系统盘,没配轮转,结果一个月后磁盘满了,数据库直接 crash。嗯,这种事故其实完全可以避免。
日志轮转的核心逻辑:写满就切,切完就删(或压缩)。
常见的轮转策略:
- 按大小轮转:每个日志文件 100MB,写满后新建一个。保留最近 10 个文件,旧的自动删除。
- 按时间轮转:每小时或每天切一次。比如
app.log.2025-03-20、app.log.2025-03-21。 - 混合策略:同时限制大小和时间。哪个先到就切。
以 Python 的 logging.handlers.RotatingFileHandler 为例:
import logging
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler(
"pipeline.log",
maxBytes=100 * 1024 * 1024, # 100MB
backupCount=10 # 保留 10 个备份
)
logging.basicConfig(handlers=[handler], level=logging.INFO)
归档策略就更进一步了。轮转出来的旧日志,不能直接扔,得存起来。我建议按这个节奏:
| 时间范围 | 存储方式 | 说明 |
|---|---|---|
| 最近 7 天 | 热存储(SSD) | 快速查询,用于日常排查 |
| 7 ~ 30 天 | 温存储(HDD) | 压缩存储,用于周报分析 |
| 30 天以上 | 冷存储(对象存储) | 归档到 S3 或 OSS,用于审计 |
最后,别忘了给日志文件加个唯一标识。我习惯在文件名里带上 pipeline_id 和 hostname,这样即使日志分散在多台机器上,也能快速定位到具体实例。
总结一下今天的核心:结构化日志是基础,级别选对是关键,轮转归档是保障。这三件事做好了,你的数据流水线才算有了「眼睛」和「记忆」。