日志写得越详细,排障反而越慢
一次线上事故,几台服务器上各有一份日志文件,每份几百兆。值班工程师用了近两个小时才定位到问题,而事后复盘时大家发现,真正有用的那几行其实一直都在日志里。
这是分布式系统里非常典型的场景。人们第一反应是加更多日志,但问题从来不是信息太少,而是信息无法被检索、无法被关联、无法被排序。
没有关联标识的日志等于散落的碎片
一次前端请求可能穿过六七个服务。如果每个服务只是各自打印自己的日志,出问题时你只能在时间轴上做近似匹配,靠猜把散落的行拼成一次请求。
解决办法是把请求级标识贯穿全链路:入口生成一个唯一编号,随调用向下传递,所有服务、所有中间件都把它打进日志。这样一条查询就能拉出整条链路,排障从人工拼图变成按编号取数。
有团队的统计显示,传统方式下平均故障定位时间长达两到四小时,其中七成花在收集和关联日志上。链路标识解决的正是这七成。
把文本日志变成可查数据
另一类常见障碍是格式。像「支付失败,用户 12345,金额 149.99」这样的自由文本,人读起来没问题,机器却要为正则表达式写一堆解析规则,字段稍有变化就失配。
改成分层字段后就完全不同:级别、事件名、用户标识、金额、链路编号各占一个字段。查询可以直接写「用户等于某值且级别为错误」,不用再靠模糊匹配碰运气。
字段命名必须跨服务统一。当支付服务写 userId、鉴权服务写 uid、通知服务写 user_id 时,一句跨服务查询就写不出来,只能分别去查再手工合并。这类不一致是排障效率最隐蔽的杀手。
级别滥用会淹没真正的错误
- 调试级:仅开发期需要的内部状态,比如循环变量、缓存命中情况。
- 信息级:值得长期保留的正常事件,比如请求完成、登录成功。
- 警告级:出现异常但系统已自行恢复,比如重试一次后成功。
- 错误级:确实失败且需要有人跟进,比如数据库连接被拒、外部接口持续返回失败。
一条实用的判断标准:如果你不会为此把人半夜叫起来处理,它就不该是错误级。当所有日志都标成错误时,错误级别就失去了筛选功能,告警也会跟着失效。
一次真实的定位过程
某电商的支付链路出现过一个问题:用户投诉付款成功但订单状态未更新,概率约千分之一。按老办法需要分别登录支付和订单服务,靠时间戳排查,耗时以小时计。
接入链路编号之后,流程变成:从前端拿一个失败请求的编号,一次查询拉出全部相关日志,按时间排序。结果清晰可见——支付回调确实到达,但订单服务在写状态时的一次锁等待超时,把更新操作丢掉了。
从接到投诉到确认根因,用了几分钟。值得注意的是,解决的并不是「日志不够多」的问题,而是日志能不能被当成数据来查询的问题。
几个看起来有用其实有害的习惯
第一个是把整个请求体打进日志。这不仅带来存储成本,还可能把用户隐私和凭据写进日志系统,属于合规风险。需要记录时应做字段白名单和脱敏处理。
第二个是只记结论不记上下文。只写「参数校验失败」,不写是哪个字段、期望格式是什么,等于让下一个人重新复现一遍。关键上下文应包括输入、判断条件和分支结果。
第三个是日志与指标、链路割裂。日志适合看细节,指标适合看趋势,链路适合看路径。只靠其中一种,都会在某一类问题上陷入低效。
从一次改造开始
不必一次性重构全部日志。先做两件事:给入口请求加上唯一编号并贯穿全链路;把日志格式改为结构化输出,并统一五六个核心字段名。
做完这两步再去处理一次真实故障,你会明显感觉到差异。之后再讨论采样策略、存储成本和字段规范,才会有实际的判断依据。