1. 日志分析基础
1.1 日志的定义
日志(Log)是系统、应用程序或设备在运行过程中按时间顺序记录的、带有时间戳的离散事件集合。每一条日志记录通常包含事件发生的时间、来源、级别以及描述信息,是系统运行状态的“黑匣子”。
核心定义:日志是系统行为的客观、有序、可追溯的数字化记录,是故障定位的第一手证据。
1.2 日志的重要性
在 SRE 和运维工作中,日志承担着不可替代的角色,其重要性体现在以下四个维度:
| 维度 | 说明 | 典型场景 |
|---|---|---|
| 故障定位 | 通过回溯日志时间线,还原故障发生前后的系统状态 | 服务宕机、请求超时、数据不一致 |
| 性能分析 | 统计请求耗时、资源使用率、慢查询等指标 | 接口响应变慢、CPU 飙高、内存泄漏 |
| 安全审计 | 记录用户操作、权限变更、异常登录等行为 | 越权访问、暴力破解、数据泄露 |
| 业务监控 | 跟踪业务转化率、订单状态、支付回调等关键链路 | 订单丢失、支付失败、用户注册异常 |
SRE 经验:在故障定位中,日志是唯一能提供“发生了什么”和“什么时候发生”双重信息的可靠数据源。指标(Metrics)告诉你系统“病了”,日志告诉你“病因”。
1.3 日志的级别(Level)
日志级别用于标识事件的严重程度和紧急程度,帮助运维人员快速过滤和聚焦关键信息。绝大多数日志框架(如 Log4j、Python logging、rsyslog)都遵循以下五级标准:
| 级别 | 数值 | 含义 | 典型内容 | 运维响应 |
|---|---|---|---|---|
| DEBUG | 10 | 调试信息,开发阶段使用 | 变量值、函数入口/出口、SQL 语句 | 生产环境通常关闭 |
| INFO | 20 | 正常运行时的重要事件 | 服务启动、请求到达、任务完成 | 日常监控关注 |
| WARN | 30 | 潜在问题,不影响当前功能 | 磁盘使用率 > 80%、重试请求、配置降级 | 需要关注并排查 |
| ERROR | 40 | 功能异常,部分请求失败 | 数据库连接失败、API 返回 500、超时 | 立即响应,触发告警 |
| FATAL | 50 | 严重错误,系统可能崩溃 | OOM、主进程退出、关键服务不可用 | 紧急介入,P0 级故障 |
常见误区:不要将所有日志都设为 ERROR 级别,否则 ERROR 日志会淹没在噪音中,失去告警区分度。合理的做法是:ERROR 只记录“需要人工介入”的异常,WARN 记录“需要关注但不紧急”的异常。
1.4 日志级别选择原则
在实际开发与运维中,日志级别的选择应遵循以下原则:
- DEBUG:仅在开发和测试环境开启,生产环境默认关闭,避免磁盘 I/O 和存储压力。
- INFO:记录关键业务节点和系统状态变更,如服务启动、配置加载、定时任务执行。
- WARN:记录非预期的但可自动恢复的情况,如重试、降级、限流触发。
- ERROR:记录业务或系统层面的失败,需要人工介入排查,如数据库连接失败、第三方接口异常。
- FATAL:记录导致进程或服务无法继续运行的灾难性错误,如内存溢出、关键依赖不可用。
1.5 日志级别过滤与告警策略
在日志收集与告警系统中,通常按级别设置不同的处理策略:
| 日志级别 | 存储策略 | 告警方式 | 保留周期 |
|---|---|---|---|
| DEBUG | 不存储或短期存储 | 不告警 | 1 天 |
| INFO | 全量存储 | 不告警 | 7-30 天 |
| WARN | 全量存储 | 聚合告警(如 5 分钟内出现 10 次) | 30 天 |
| ERROR | 全量存储 + 索引 | 即时告警(电话/短信/IM) | 30-90 天 |
| FATAL | 全量存储 + 持久化 | 紧急告警(电话 + 值班组长) | 90 天以上 |
1.6 日志级别关系图
下图展示了日志级别从低到高的递进关系,以及各级别对应的典型事件和响应方式:
1.7 日志级别配置示例
以下是一个 Python 日志配置示例,展示了如何按级别设置不同的处理器和格式:
import logging
# 创建日志器
logger = logging.getLogger('sre_course')
logger.setLevel(logging.DEBUG)
# 控制台处理器:输出 INFO 及以上级别
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_format = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s')
console_handler.setFormatter(console_format)
# 文件处理器:输出 DEBUG 及以上级别,用于详细排查
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)
file_format = logging.Formatter('%(asctime)s [%(levelname)s] %(name)s - %(message)s')
file_handler.setFormatter(file_format)
# 错误文件处理器:只记录 ERROR 和 FATAL
error_handler = logging.FileHandler('error.log')
error_handler.setLevel(logging.ERROR)
error_format = logging.Formatter('%(asctime)s [%(levelname)s] %(name)s:%(lineno)d - %(message)s')
error_handler.setFormatter(error_format)
# 添加处理器
logger.addHandler(console_handler)
logger.addHandler(file_handler)
logger.addHandler(error_handler)
# 使用示例
logger.debug('数据库查询 SQL: SELECT * FROM users')
logger.info('用户登录成功: user_id=12345')
logger.warning('磁盘使用率已达 85%')
logger.error('数据库连接超时,重试第 3 次')
logger.fatal('内存溢出,进程即将退出')
最佳实践:生产环境中建议将 DEBUG 日志写入独立文件,并设置日志轮转(log rotation)避免磁盘写满。ERROR 和 FATAL 日志应同时输出到标准错误流(stderr),便于容器化环境下的日志采集。
1.8 日志级别与故障定位的关联
在故障定位过程中,日志级别直接决定了排查效率:
- 快速过滤:先查看 ERROR 和 FATAL 日志,确定故障范围和影响面。
- 上下文回溯:根据 ERROR 时间点,向前查看 WARN 和 INFO 日志,寻找前置异常信号。
- 根因分析:在 DEBUG 日志中定位具体的代码路径、参数值和异常堆栈。
- 趋势判断:通过 WARN 日志的频率变化,预判系统是否正在恶化。
核心结论:日志级别是故障定位的“导航仪”——ERROR 告诉你“哪里出了问题”,WARN 告诉你“哪里可能出问题”,DEBUG 告诉你“为什么出问题”。合理使用日志级别,能将故障定位时间从小时级缩短到分钟级。