日志规范

1. 日志分类

日志:用来准确、恰当、适量地记录用户操作、系统运行状态等,是一个系统的重要组成部分

日志从功能划分,可分为诊断日志统计日志审计日志

  • 诊断日志
    • 请求的入口和出口
    • 外部服务调用及返回
    • 资源消耗操作: 打开文件等
    • 容错行为,譬如云硬盘的副本修复操作
    • 程序异常,譬如数据库无法连接
    • 后台操作,清理程序
    • 启动、关闭、配置加载
    • 抛出异常时,不记录日志
  • 统计日志
    • 用户访问统计
    • 计费日志(如记录用户使用的网络资源或磁盘占用,格式较为严格,便于统计)
  • 审计日志
    • 管理操作

2. 日志规范重要性

只有在程序出问题以后才会知道打一个好的日志有多么重要

低效日志将导致开发/测试/运维人员:

  • 对系统的运行状态一知半解,甚至一无所知
  • 系统出现问题无法定位,或需耗费大量时间和精力
  • 无法发现系统瓶颈,不知优化从何做起
  • 无法基于日志对系统运行过程中的错误和潜在风险进行监控和报警
  • 对挖掘用户行为和提升产品价值毫无帮助

优质日志将帮助开发/测试/运维人员:

  • 准确知晓线上系统的运行状态
  • 快速定位线上问题,高效排障
  • 发现系统瓶颈
  • 预警系统潜在风险
  • 挖掘产品最大价值

3. 日志规范总则

  • 减少日志中无用信息,防止淹没重要信息

  • 要尽可能使日志信息准确全面,以便快速精确定位问题

  • 要统一日志格式规范,以方便后续排障和分析

  • 要不断优化完善日志,以提升问题定位效率

  • 要明确不同功能日志的用途,使日志内容符合对应功能日志的要求

  • 开发过程中记录日志应思考:

    • 该行日志真的有人看么?
    • 该条日志能够传递什么信息?
    • 这条日志能对排障带来什么帮助?

4. 日志规范

4.1. 日志级别的选择规约

如何针对不同场景,选择恰当的日志级别?

  • Emergency
    • 发生导致系统不可用的事故时
    • 一个进程的生命周期里最多记录一次 Emergency 级别日志(慎用)
  • Alert
    • 发生核心功能不可用的紧急事件时
    • 紧急程度仅次于 Emergency,此时系统虽仍可用,但已严重影响了功能的完整性与可用性
    • 必须马上处理
  • Critical
    • 发生程序组件不可用的危急事件时
    • 需要马上处理
  • Error
    • 程序运行中出现错误,但不影响整个系统的逻辑运行时
    • 一般不需要立即修复,但必须及时记录并做检测
  • Warning
    • 发生若不及时处理可能引发/导致程序出错的征兆事件时
    • 虽暂时未发生程序错误,但也需要提醒并及时查看和处理
  • Notice
    • 发生不影响程序功能,但需要引起注意的事件时
  • Informational
    • 记录系统正常运行的一般信息,侧重对正常运行的主要流程的记录
    • 辅助更高级别错误日志的定位分析,譬如排除掉Error错误前已打Info日志的正常流程,缩小排障范围
  • Debug
    • 帮助开发/测试/运维人员,对系统进行诊断的更丰富和细致的信息

日志级别选择流程:

CloudComputing-20190617100822880

4.2. 日志格式规约

如何利用正确的日志格式规范所输出的日志内容?

4.2.1. 日志格式总体要求

  • 统一字段命名:对于不同请求中的同一含义的字段,只能有一个名字
  • 统一字段风格:譬如字段一律使用 xxx_yyy 的下划线命名风格
  • 统一字段顺序:譬如统一使用 请求ID/服务名/请求参数/响应数据/响应时间 作为日志字段顺序

4.2.2. 格式规则

  • 避免字符串拼接,使用‘{‘和’}’作为消息正文中参数的占位符
  • 避免重复记录,同样的日志内容理论上只需要记录一次,否则会造成磁盘空间浪费,过多冗余日志也会对查找问题产生干扰
  • 日志一定要有显示Level呈现,一眼就知道日志属性
  • 重要日志信息脱敏,如用’*’号代替

4.2.3. 日志格式

即日志的字段结构,包含哪些字段,字段顺序等


示例日志格式

1
2021-08-31 20:35:01.713966 host <local2.notice> NOTICE client[38] zread_hello(): client 29 says hello and bids fair.
日志结构中各字段 对应日志内容
日志时间 2021-08-31 20:35:01.713966
主机名 host
日志属性<模块类别.日志级别> <local2.notice>
日志级别 NOTICE
所属进程/线程 client[38]
类名/方法名 zread_hello():
日志正文 client 29 says hello and bids fair.

4.3. 日志内容规约

什么场景下打日志,日志输出什么内容?

4.3.1. 推荐记录的日志内容

1. 系统启动或初始化时,重要的系统初始化参数、配置加载【Info】

2. 系统运行过程中的重要信息

  • 所有错误信息
    • 程序崩溃事故【Emergency】
    • 核心功能不可用紧急事件【Alert】
    • 程序组件不可用危急事件【Critical】
    • 程序运行错误【Error】
  • 所有警告信息【Warning】:
    • 暂未但不及时处理可能将会导致错误的事件
    • 流程中正常的请求出错
  • 关键流程信息【Info】:
    • 主要模块间的请求与响应
    • 重要事件的发生与结束
    • 重要状态变化
    • 持久化数据时修改前后的记录变化
    • 长期执行任务的执行进度
  • 后台定期执行任务【Info】:
    • 定期缓存更新任务的启停时间
  • 异常处理逻辑【Notice】:
    • 请求资源首次未成功后,后续尝试再次请求的行为记录

3. 有助于排障运维的信息【Debug】

  • 重要事件函数的启停,返回值信息等
  • 核心变量的变化
  • 内存数据结构的状态

4.3.2. 避免记录的日志内容

  • 文件内容或者一大段消息的内容
  • 良性“错误”,有时候尽管出现了错误,然而错误处理的流程可以正确解决这种情况
  • 预期会发生且能够被正常处理的异常,否则将打印出一堆无用的堆栈信息
  • 开发者为方便调试临时加入的非必要状态信息
  • 输出机密信息

参考文档