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 日志应该直接触发告警。
我的经验法则:ERROR 是「现在就要修」,WARN 是「明天之前看看」,INFO 是「事后复盘用」,DEBUG 是「出问题时才开」。

还有一个容易被忽略的点:日志级别的动态调整。生产环境默认只开 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,用于审计
避坑指南:我曾经把归档周期设成 90 天,结果日志量暴涨,存储成本翻了 3 倍。后来改成 30 天热存 + 90 天冷存,成本降了 60%。归档策略一定要结合业务需求和数据量来定,别一刀切。

最后,别忘了给日志文件加个唯一标识。我习惯在文件名里带上 pipeline_id 和 hostname,这样即使日志分散在多台机器上,也能快速定位到具体实例。

日志管理核心流程 应用服务 日志生成(结构化) 级别过滤(INFO+) 级别过滤(DEBUG) 轮转(大小/时间) 临时开启(排查用) 归档(热/温/冷) 💡 归档策略建议: 7天热存 → 30天温存 → 90天冷存

总结一下今天的核心:结构化日志是基础,级别选对是关键,轮转归档是保障。这三件事做好了,你的数据流水线才算有了「眼睛」和「记忆」。