日志系统概述
日志这东西,说白了就是程序跑起来留下的脚印。你想想看,一个线上系统跑了大半年,突然某天半夜报警说接口超时了——你第一反应是什么?肯定是翻日志啊。我做了这么多年后端,可以说九成以上的线上问题都是靠日志来定位的。没有日志,你就像在黑屋子里修电路,摸不着头脑。
但很多团队对日志的态度是「能打出来就行」,格式随意、级别乱用、输出量失控。等到真正需要用它的时候,又发现要么信息不全,要么日志把磁盘撑爆了。嗯,这一章我们就来好好捋一捋日志系统的基础——它的作用、级别和格式。把这些地基打牢了,后面的采集和优化才不会跑偏。
日志到底能干什么
别觉得这个问题太简单。我见过不少项目,日志就是用来「打印变量值」的。其实日志的价值远不止这些,我个人习惯把它分成四个维度来看:
- 故障排查——最直接的用途。系统出问题了,日志就是你的「黑匣子」。调用链路、参数、异常堆栈,缺一不可。
- 系统监控——通过日志的产出量、错误率、响应时间等指标,判断系统是否健康。比如每分钟 ERROR 日志突然飙高,那多半有情况。
- 安全审计——谁在什么时间做了什么操作,日志里必须有记录。这在金融、合规类项目里是硬要求,跑不掉的。
- 业务分析——从日志里提取用户行为、订单数据、转化率等。说白了,日志不只是给程序员看的,也能给产品和运营用。
我的经验之谈: 有一次线上出了数据一致性问题,开发同事查了两天没找到根因。后来我翻了那个服务三个月的 WARN 级别日志,发现某个边界条件在特定数据量下会触发脏写。这件事之后,我们团队把「保留 90 天 WARN + ERROR 日志」写进了规范。
日志级别:信息的分级管理
日志级别这个东西,本质上是给信息贴了个「严重程度」的标签。你想想看,如果所有信息都混在一起,那跟没有日志也没什么区别。Java 生态里最常见的是 6 个级别,从低到高分别是:
| 级别 | 数值 | 典型用途 | 线上环境建议 |
|---|---|---|---|
| TRACE | 0 | 极细粒度追踪,比如每一步的变量值 | 关闭,除非你在调试特定模块 |
| DEBUG | 1 | 开发调试信息,比如方法入口出口 | 关闭,留给开发和测试环境用 |
| INFO | 2 | 常规操作记录,比如订单创建成功 | 开启,这是线上主力级别 |
| WARN | 3 | 潜在问题提醒,不影响当前运行 | 开启,值得重点关注 |
| ERROR | 4 | 错误事件,功能受损但服务存活 | 必须开启,需要立刻处理 |
| FATAL | 5 | 致命错误,系统即将或已经崩溃 | 必须开启,通常伴随告警 |
曾经踩过的坑: 有次我把一个内部工具的日志级别设成了 DEBUG 就上线了,结果一天下来日志文件写了 200 多 GB,直接把磁盘打满了,连带着监控系统都挂了。后来我养成了一个习惯——上线前必须过一遍日志配置文件,确保线上只开 INFO 及以上级别。
级别之间是包含关系。比如你设置日志级别为 WARN,那么 WARN、ERROR、FATAL 都会输出,而 TRACE、DEBUG、INFO 会被过滤掉。这个「向上包含」的规则,每个开发者都应该刻在脑子里。
小建议: 开发环境用 DEBUG,测试环境用 INFO,线上环境用 INFO 或 WARN。TRACE 只在定位疑难杂症时临时开启,定位完立刻关掉。这一点最好写进团队的开发规范里。
日志格式标准:统一才能高效
日志格式看似是小事,但格式不统一,后面做采集和解析的时候会非常痛苦。我见过一个项目,同一个服务里有人用 JSON,有人用纯文本,有人又用管道符分割——写日志的人倒是方便了,但运维同学想从里面提取一个字段,得写三套正则。
一个标准的日志格式,至少应该包含以下几个字段:
- 时间戳——精确到毫秒,带时区。这是排序和定位的关键。
- 日志级别——便于过滤和告警。
- 线程名——并发场景下追踪请求的必经之路。
- 类名/方法名——出问题了你得知道是哪里出的。
- 消息体——具体的业务信息。
- 异常堆栈——ERROR/FATAL 级别必须附带。
下面是一个我个人比较推荐的格式示例(Logback 配置):
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
它输出的日志长这样:
2025-01-15 14:30:25.123 [http-nio-8080-exec-3] INFO com.example.OrderService - 订单创建成功,订单号: ORD20250115001
你看,时间、线程、级别、类名、消息,一眼扫过去就能抓住重点。如果你用的是 JSON 格式,那就更规范了,采集端解析起来也省力:
{
"timestamp": "2025-01-15T14:30:25.123+08:00",
"level": "INFO",
"thread": "http-nio-8080-exec-3",
"logger": "com.example.OrderService",
"message": "订单创建成功,订单号: ORD20250115001",
"traceId": "a1b2c3d4e5f6"
}
个人习惯: 我倾向于在新项目里直接上 JSON 格式。虽然日志体积会大一些,但采集端解析几乎没有成本,而且扩展起来很方便——加个字段就行了,不需要改解析规则。
一张图看懂日志系统
下面这张图把日志系统的三个核心维度串在了一起。你可以把它当作本章的知识索引,也可以贴在工位旁边当参考:
聊几句实践中的体会
基础概念讲完了,最后跟你分享几点我这些年总结的体会:
- 别把日志当控制台。 System.out.println 不是日志,它没有级别、没有格式、没有归档策略。用正规的日志框架,比如 Logback、Log4j2,这是基本底线。
- 日志量要可控。 线上环境 INFO 就够了,别开 DEBUG。我见过一个团队把 TRACE 开到线上,结果一天的日志量是正常情况的 50 倍,ELK 集群直接被打挂了。
- 格式统一是团队的事。 一个项目的日志格式应该写在规范文档里,而不是让每个开发自己决定。不然你后面做日志采集的时候,光写解析规则就够你喝一壶的。
- 别忘了日志归档和清理。 日志不清理,磁盘总会满的。按天滚动、保留 30 天或 90 天,这个策略在项目启动时就要定好。
最后说一句: 日志系统看似基础,但它是整个可观测性的基石。你想想看,监控告警、链路追踪、审计分析,哪一样离得开日志?这一章我们把概念对齐了,后面讲采集配置和性能优化的时候,你就能更快上手了。