背景
最近由于组织架构调整,团队引入不少新成员。这些成员来自不同的团队和背景,每个人的日志打印风格难免也有所差异,十分有必要制定一个统一的日志打印规范。
规范日志打印主要有以下作用:
- 让系统运行状态更透明,提高线上故障排查效率。
- 减少代码风格差异,利于代码阅读和维护。
- 统一日志格式,利于外围工具(日志监控报警工具等)处理。
团队成员在研发过程中应该遵守该规范,文档内容可能会与你的喜好冲突,请尽量用包容的心态接受。如果有不合理之处,请先在组内提出建议,组内一致表决通过后修改规范。
日志的定义
日志用来记录系统运行状态,是系统的重要组成部分。
系统的透明度反映的系统的运维难度,可以理解为系统越透明,越容易运维。日志是提升系统透明度的重要的手段之一。
日志级别
日志级别 | 是否默认开启 | 是否需要监控 | 适用场景 |
---|---|---|---|
TRACE | 否 | 否 | 跟踪运行代码,通常是函数调用级别。日志量最大,线上通常不开启。 |
DEBUG | 否 | 否 | 记录对诊断有帮助的信息,通常面向开发人员和运维人员。日志量较大,线上通常不开启。 |
INFO | 是 | 否 | 记录系统正常运行状态下的有用信息。如服务启动/停止,配置加载等,日志量相对较大,线上默认开启。 |
NOTICE | 是 | 否 | 记录需要常态打印的重要事件。日志量可控,比如请求接口的输入输出日志。 |
WARN | 是 | 是,只需要监控部分重要的日志 | 记录系统可能正常出现的某种异常,这种异常不应该影响业务主流程,且对用户是无感知的。这种异常通常是可以自动恢复的,当异常持续出现时,需要及时查看并处理。比如因为网络抖动造成短时间内读取缓存失败。 |
ERROR | 是 | 是,需要监控全部日志 | 记录系统运行错误,这种错误会中断主流程,已经影响到用户的正常访问,紧急程度仅次于FATAL。这个级别的错误是可以恢复的,出现该级别错误时需要马上跟进处理。如数据库写入失败。 |
FATAL | 是 | 是,需要监控全部日志 | 记录最严重的系统级错误。出现这种级别的错误时,表示整个服务已经出现了某种程序的不可用。出现这个级别的错误时需要系统管理员立即介入。通常一个进程的生命周期中应该只记录一次FATAL级别的日志,即该进程遇到无法恢复的错误而退出。 |
日志格式
一个好的日志,应该记录刚刚好的能反映当前事件的重要信息,主要包括如下:
- 发生了什么事件
- 事件的状态是什么
- 什么原因触发了该事件
- 事件的关键上下文信息
- 延迟是多少(如果适用)
- 重复度情况(如果适用)
示例:
1 | "upload render data failed due to io timeout,render_id:xxx object_key:xxx auth_info:xxx" |
上述示例中:
- “upload render data failed” 是发生的事件及状态
- “due to io timeout” 是触发事件的原因
- “render_id:xxx object_key:xxx auth_info:xxx” 是事件的关键上下文信息
事件的关键上下文信息主要用于排查问题,而不至于在多个日志中去寻找相关信息,提高排查问题的效率。
一些细则
以下约定不存在正确与否,团队内成员共同表决,达成一致即可。
重要单词说明
词汇 | 名词 | 动词 | 形容词 |
---|---|---|---|
成功 | success | succeed | succeeded |
失败 | failure | fail | failed |
事件及状态的描述语法
示例 | 理由 | 结论 |
---|---|---|
upload render data failed | 事件为主体,状态是事件的属性 | V |
fail to upload render data | X |
主客体信息的展示
示例 | 理由 | 结论 |
---|---|---|
render_id=123456 | 不满足大部分人的习惯 | X |
render_id= 123456(等号后面有一个空格) | 空格为导致awk时列数太多 | X |
render_id = 123456(等号前后有一个空格) | 空格为导致awk时列数太多 | X |
render_id: 123456(冒号后面有一个空格) | 空格导致awk时列数太多 | X |
render_id:123456 | 符合大多数人习惯 | V |
连续主客体之间是否需要逗句
示例 | 理由 | 结论 |
---|---|---|
render_id:xxx, object_key:xxx | 如果逗号后面有空格,则逗号没有必要; 如果逗号后面没有空格,视觉上又太密集 |
X |
render_id:xxx object_key:xxx | V |
首字母是否需要大写
示例 | 理由 | 结论 |
---|---|---|
upload render data failed | 日志可能存在拼接,统一小写,拼接后的日志存在非首字母大写问题 | V |
Upload render data failed | X |
句尾是否需要标点符号
日志尽量采用陈述句,明确表明日志事件的行为。
示例 | 理由 | 结论 |
---|---|---|
upload render data failed. | 日志可能存在拼接,在日志拼接时决定标点符号 | X |
upload render data failed | V |
几个重要的建议
以下几个建议都是一些经验总结,没有足够的理由不要违反:
- Restful API日志/RPC日志:一定要打印出LOGID(不同环境可能是request_id,trace_id)、目的端地址、本次交互延迟、累计延迟、重试次数。
- 磁盘读写日志:一定要打印出延迟,如果读写出错,一定要打印出Linux的errno。
- 系统调用出错:一定要打出Linux的errno。
- 服务启动/停止:一定要打出服务启动和停止日志,启动时打印加载的配置信息。
参考资料
[1] 最佳日志实践v2.0
[2] Logging Best Practices: The 13 You Should Know
[3] When to use the different log levels