日志分析:从级别到轮转

日志这东西,平时你可能不觉得它有多重要。一旦系统出了故障,它就是你手里最锋利的工具。我在这行干了十几年,可以负责任地说:日志质量直接决定你排查问题的效率。

说白了,日志就是系统在运行过程中的“黑匣子”。怎么记录、记录什么、记录多久,都需要你提前规划好。

日志级别:该用什么就用什么

为什么有的日志我压根不看?因为全都是 DEBUG 信息。你想想看,生产环境出问题了,几千行 DEBUG 日志翻得过来吗?

常见的日志级别从低到高是这样的:

级别 含义 我的用法
DEBUG 调试信息 开发时开启,上线就关掉。信息量太大
INFO 正常流程关键节点 服务启动、请求到达、任务完成这类
WARN 潜在问题 配置不推荐、重试次数过多、磁盘快满了
ERROR 明确异常 数据库连不上、接口超时、逻辑处理失败

我在项目中遇到过有人把所有日志都打成 ERROR。结果呢?真正的 ERROR 被淹没在一堆“连接断开又重连成功”的日志里。

一个小建议:线上环境默认设置成 INFO 或 WARN 就够了。DEBUG 日志只在排查具体问题时临时打开,用完就关。

举个例子,Java 项目里配置日志级别:

# logback.xml
<root level="INFO">
    <appender-ref ref="FILE" />
</root>

<logger name="com.example.mapper" level="DEBUG" />

这里我单独给某个包开了 DEBUG,不会影响全局。

日志格式规范:让人一看就懂

常见的日志长什么样?看看下面两个对比:

差劲的格式:

[ERROR] 用户登录失败

谁登录失败了?是什么请求失败了?查到这句日志你什么都干不了。

合理的格式:

2024-03-15 14:23:45.678 [http-nio-8080-exec-3] ERROR c.e.a.LoginController - 用户登录失败 | userId=10086 | 原因: 密码错误 | ip=192.168.1.10

看到了吗?时间、线程、类名、请求 ID、上下文信息,一目了然。我个人特别喜欢带上下文的日志格式,排查问题能省一半时间。

字段 说明 必须吗
时间戳 带毫秒或微秒,最好有时区 必须
日志级别 用固定长度的缩写,方便 grep 必须
线程号/请求 ID 高并发下区分请求来源 强烈建议
类名/方法名 快速定位代码位置 建议
上下文信息 用户 ID、订单号、参数摘要 必须
避坑指南:我曾经看过有人把整个对象序列化后打出来,日志文件几分钟就炸了。日志里只放关键字段,别偷懒。

建议团队统一日志格式。我习惯用这种方式:

时间 [线程] 级别 类名 - 消息 | key1=value1 | key2=value2

这样用 grep 和 awk 都能很方便地筛选:

grep "ERROR" app.log | grep "userId=10086"

日志轮转策略:别让日志撑爆磁盘

日志如果不轮转,结果只有一个:磁盘写满,系统宕机。这不是开玩笑,我就亲眼见过生产环境因为日志太大把根分区塞满的惨案。

日志轮转说白了就是:按时间或大小把日志切成多份,老的自动清理或压缩。我最常用的工具是 Linux 自带的 logrotate。

举个例子:

/var/log/myapp/*.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
}

这段配置什么意思?每天轮转一次,保留最近 7 天的日志,旧日志压缩,空文件不处理。搞定了。

策略 场景 建议值
按大小 日志量不稳定,需要控制单个文件大小 100M - 1G
按时间 需要按天/周归档审计 每天或每周
保留数量 考虑磁盘空间和审计要求 7 - 30 份
压缩 节省空间,但排查时需解压 建议开启
一个小技巧:日志单独的磁盘分区,别和系统分区混一起。就算日志炸了,系统还能撑住。

有些框架自带轮转,比如 Python 的 RotatingFileHandler:

import logging
from logging.handlers import RotatingFileHandler

handler = RotatingFileHandler(
    'app.log', maxBytes=10*1024*1024, backupCount=5
)
logging.basicConfig(handlers=[handler])

这样单个文件不超过 10MB,最多保留 5 个备份。

我再提醒一句:轮转时如果应用正在写日志,可能导致文件被删或日志丢失。大部分框架用 copytruncate 模式解决这个问题。嗯,这里要注意测试环境验证一下。

一张图理清日志体系

下面这张图展示了日志从产生到归档的完整流程:

日志处理核心流程 应用服务 级别筛选 格式化输出 日志文件 轮转策略 归档/清理 核心原则: 1. 生产环境只保留 INFO+ 级别,DEBUG 日志仅在排查时开启 2. 格式规范统一,包含时间、线程、上下文等关键字段 3. 必须配置轮转策略,防止磁盘被日志写满

从应用产生日志,到最终归档清理,每个环节都不该马虎。级别选错了,信息丢了一堆。格式不规范,排查时两眼一抹黑。不配轮转,磁盘迟早炸掉。

我个人习惯在项目初期就定好日志规范,写在团队 Wiki 里。时间长了,大家形成习惯,整个团队的排查效率都会上一个台阶。