日志基础:日志级别、格式标准与存储位置
日志这东西,说白了就是系统的「黑匣子」。升级失败时,你第一个想到的肯定是翻日志。但日志如果写得乱七八糟,翻也白翻。今天我就把日志的基础掰开揉碎了讲清楚。
日志级别:从 DEBUG 到 FATAL
日志级别定义了信息的严重程度。不同级别对应不同场景,用错了会出大问题。
| 级别 | 含义 | 典型场景 | 生产环境建议 |
|---|---|---|---|
| DEBUG | 调试信息 | 变量值、函数入口/出口、循环次数 | 关闭 |
| INFO | 常规信息 | 服务启动、请求到达、任务完成 | 开启 |
| WARN | 警告 | 磁盘使用率超 80%、重试次数过多 | 开启 |
| ERROR | 错误 | 数据库连接失败、请求超时、业务异常 | 开启 |
| FATAL | 致命错误 | 内存溢出、配置加载失败、进程崩溃 | 开启 |
核心原则:生产环境只开 INFO 及以上级别。DEBUG 日志量太大,会拖垮磁盘 IO。我见过有人把 DEBUG 开在生产环境,结果一天写了 200GB 日志,直接把磁盘撑爆了。
每个级别都有它的「脾气」。我习惯这样用:
- DEBUG:开发时用,上线前必须关掉。我曾经排查一个内存泄漏,就是靠 DEBUG 日志定位到某个对象没释放。
- INFO:记录系统正常运转的关键节点。比如「用户登录成功」「订单创建完成」。别什么都打 INFO,否则跟没打一样。
- WARN:系统还能跑,但需要关注。比如「重试第 3 次成功」。WARN 是预警信号,别忽略它。
- ERROR:功能受损,但系统没挂。比如「数据库查询超时,返回降级数据」。ERROR 必须带堆栈,否则查不了根因。
- FATAL:系统扛不住了。比如「无法加载核心配置文件,进程退出」。FATAL 日志一出现,就该立刻拉警报。
避坑指南:我曾经接手过一个项目,所有异常都打 ERROR,连参数校验失败也打 ERROR。结果线上出了真故障,几千条 ERROR 日志里根本找不到关键信息。级别用错了,日志就是噪音。
日志格式标准:统一才能分析
日志格式不统一,分析工具就没法用。你想想看,如果每行日志的字段顺序都不一样,grep 都救不了你。
我推荐的标准格式:
时间戳 | 线程ID | 日志级别 | 类名:行号 | 请求ID | 消息内容 | 异常堆栈
举个例子:
2025-03-21 14:32:18.123 | http-nio-8080-exec-3 | ERROR | com.example.OrderService:156 | req-abc123 | 订单支付超时,订单号:ORD20250321001 | java.net.SocketTimeoutException: Read timed out
at com.example.OrderService.pay(OrderService.java:160)
at com.example.OrderController.submit(OrderController.java:45)
为什么这么设计?
- 时间戳:精确到毫秒,方便定位时间点。我习惯用 yyyy-MM-dd HH:mm:ss.SSS,别用时间戳数字,人眼看不懂。
- 线程ID:多线程环境下,知道哪个线程出的问题。特别是 Web 应用,一个请求一个线程。
- 日志级别:一眼看出严重程度。grep ERROR 就能过滤出所有错误。
- 类名:行号:快速定位代码位置。没有这个,你都不知道去哪修。
- 请求ID:串联一次请求的全链路日志。这是排查分布式问题的关键。
- 消息内容:描述发生了什么。要具体,别写「发生异常」这种废话。
- 异常堆栈:ERROR 和 FATAL 必须带。没有堆栈,你只能猜原因。
小技巧:请求ID 可以用 UUID 或 Snowflake 算法生成。在请求入口处生成,然后通过 MDC(Mapped Diagnostic Context)传递到日志框架。这样一次请求的所有日志都带上同一个 ID,排查起来特别爽。
日志存储位置:放对地方才能找到
日志存哪?这个问题看似简单,但坑不少。我见过有人把日志和程序放在同一个磁盘,结果日志写满,程序直接挂了。
标准做法:
/var/log/<应用名>/
├── app.log # 当前日志
├── app.log.2025-03-21.gz # 历史日志(已压缩)
├── error.log # 只包含 ERROR 和 FATAL
├── access.log # 访问日志(Web 应用)
└── gc.log # JVM GC 日志(Java 应用)
几个关键点:
- 独立磁盘:日志目录单独挂载一块磁盘。系统盘写满了,至少应用还能跑。
- 按天滚动:每天一个文件,保留 30 天。用 logrotate 或框架自带的滚动策略。
- 压缩归档:历史日志用 gzip 压缩,能省 80% 空间。我习惯保留 90 天的压缩日志,方便回溯。
- 分离错误日志:单独一个 error.log 只存 ERROR 和 FATAL。排查问题时直接看这个文件,不用在 INFO 里翻。
避坑指南:我曾经遇到一个案例,日志文件没有做滚动,结果一个文件写了 50GB。用 vim 打开直接卡死,grep 也慢得要命。从那以后,我强制所有项目必须配置日志滚动,最大文件不超过 500MB。
知识体系总览
下面这张图把日志基础的核心知识点串起来了。你可以把它当作一张「日志排查地图」。
这张图把日志级别、格式、存储三个维度串在一起了。你排查升级失败时,按这个框架去检查日志配置,基本不会漏掉关键点。
我的习惯:每次接手一个新系统,我第一件事就是看日志配置。级别对不对?格式全不全?存哪了?滚动策略有没有?这三个问题问完,心里就有底了。升级失败时,日志就是你最可靠的战友。