大家好,我是苏三,又跟大家见面了。
前言
去年双十一大促,我面对监控大屏上疯狂跳动的红色指标,颤抖着打开服务器日志,看到的却是这样的画面:
那一刻我突然顿悟:写不好日志的程序员,就像不会写病历的医生。
这篇文章跟大家一起聊聊打印优质日志的10条军规,希望对你会有所帮助。
第1条:格式统一
反例(管理看到会扣钱):
无时间戳,无上下文。
正解代码:
在logback.xml中统一配置了日志的时间格式、tradeId,线程、等级、日志详情都信息。
日志的格式统一了,更方便点位问题。
第2条:异常必带堆栈
反例(同事看了想打人):
出现异常了,日志中没打印任何的异常堆栈信息。
相当于自己把异常吃掉了。
非常不好排查问题。
正确姿势:
日志中记录了出现异常的订单号orderId和异常的堆栈信息e。
第3条:级别合理
反面教材:
接口响应稍慢,打印了error级别的日志,显然不太合理。
正常情况下,普通超时属INFO级别。
级别定义表:
级别 | 正确使用场景 |
FATAL | 系统即将崩溃(OOM、磁盘爆满) |
ERROR | 核心业务失败(支付失败、订单创建异常) |
WARN | 可恢复异常(重试成功、降级触发) |
INFO | 关键流程节点(订单状态变更) |
DEBUG | 调试信息(参数流水、中间结果) |
第4条:参数完整
反例(让运维骂娘):
上面这个日志只打印了“用户登录失败”这个文案。
谁在哪登录失败?
侦探式日志:
登录失败的业务场景,需要记录哪个用户,ip是多少,在什么时间,登录失败了,失败的原因是什么。
时间在logback.xml中统一配置了格式。
这样才方便快速定位问题:
第5条:数据脱敏
血泪案例:
某同事打印日志泄露用户手机号被投诉。
我在记录的日志中,需要对一下用户的个人敏感数据做脱敏处理。
例如下面这样:
第6条:异步保性能
问题复现
某次秒杀活动中直接同步写日志,导致大量线程阻塞:
高并发下IO阻塞。
致命伤害分析:
- 同步写日志导致线程上下文切换频繁
- 磁盘IO成为系统瓶颈
- 高峰期日志打印耗时占总RT的25%
最近建了一些工作内推群,各大城市都有,欢迎各位HR和找工作的小伙伴进群交流,群里目前已经收集了不少的工作内推岗位。加苏三的微信:li_su223,备注:优快云+所在城市,即可进群。
正确示范(三步配置法)
步骤1:logback.xml配置异步通道
步骤2:日志输出优化代码
流程图如下:
步骤3:性能关键参数公式
风险规避策略
- 防队列堆积:监控队列使用率,达80%触发告警
- 防OOM:严格约束大对象toString()的调用
- 紧急逃生:预设JMX接口用于快速切换同步模式
第7条:链路追踪
混沌场景:
跨服务调用无法关联日志。
我们需要有链路追踪方案。
全链路方案:
可以在MDC中设置traceId。
后面可以通过traceId全链路追踪日志。
流程图如下:
第8条:动态调参
半夜重启的痛:
线上问题需要临时开DEBUG日志,比如:查询用户的某次异常操作的日志。
热更新方案:
有时候我们需要临时打印DEBUG日志,这就需要有个动态参数控制了。
否则每次调整打印日志级别都需要重启服务,可能会影响用户的正常使用。
第9条:结构化存储
混沌日志:
上面的日志拼接成了一个字符串,虽说中间有空格分隔了,但哪些字段对应了哪些值,看起来不是很清楚。
我们在存储日志的时候,需要做结构化存储,方便快速的查询和搜索。
机器友好式日志:
这里使用了json格式存储日志。
日志中的数据一目了然。
第10条:智能监控
最失败案例:
某次用户开通会员操作,错误日志堆积3天才被发现,黄花菜都凉了。
我们需要在项目中引入智能监控。
ELK监控方案:
报警规则示例:
总结
研发人员的三大境界:
- 青铜:
System.out.println("error!")
- 钻石:标准化日志 + ELK监控
- 王者:
- 日志驱动代码优化
- 异常预测系统
- 根因分析AI模型
最后的灵魂拷问:
下次线上故障时,你的日志能让新人5分钟定位问题吗?