日志系统概述

日志这东西,说白了就是程序跑起来留下的脚印。你想想看,一个线上系统跑了大半年,突然某天半夜报警说接口超时了——你第一反应是什么?肯定是翻日志啊。我做了这么多年后端,可以说九成以上的线上问题都是靠日志来定位的。没有日志,你就像在黑屋子里修电路,摸不着头脑。

但很多团队对日志的态度是「能打出来就行」,格式随意、级别乱用、输出量失控。等到真正需要用它的时候,又发现要么信息不全,要么日志把磁盘撑爆了。嗯,这一章我们就来好好捋一捋日志系统的基础——它的作用、级别和格式。把这些地基打牢了,后面的采集和优化才不会跑偏。

日志到底能干什么

别觉得这个问题太简单。我见过不少项目,日志就是用来「打印变量值」的。其实日志的价值远不止这些,我个人习惯把它分成四个维度来看:

  • 故障排查——最直接的用途。系统出问题了,日志就是你的「黑匣子」。调用链路、参数、异常堆栈,缺一不可。
  • 系统监控——通过日志的产出量、错误率、响应时间等指标,判断系统是否健康。比如每分钟 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,有人用纯文本,有人又用管道符分割——写日志的人倒是方便了,但运维同学想从里面提取一个字段,得写三套正则。

一个标准的日志格式,至少应该包含以下几个字段:

  1. 时间戳——精确到毫秒,带时区。这是排序和定位的关键。
  2. 日志级别——便于过滤和告警。
  3. 线程名——并发场景下追踪请求的必经之路。
  4. 类名/方法名——出问题了你得知道是哪里出的。
  5. 消息体——具体的业务信息。
  6. 异常堆栈——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 格式。虽然日志体积会大一些,但采集端解析几乎没有成本,而且扩展起来很方便——加个字段就行了,不需要改解析规则。

一张图看懂日志系统

下面这张图把日志系统的三个核心维度串在了一起。你可以把它当作本章的知识索引,也可以贴在工位旁边当参考:

日志系统知识体系 日志系统 日志作用 故障排查 系统监控 安全审计 业务分析 日志级别 TRACE (0) DEBUG (1) INFO (2) WARN (3) ERROR (4) FATAL (5) 日志格式 时间戳 级别标识 线程名 类名方法 消息体 💡 推荐 JSON 格式 便于采集端解析与扩展 兼容性好,适合大规模部署

聊几句实践中的体会

基础概念讲完了,最后跟你分享几点我这些年总结的体会:

  • 别把日志当控制台。 System.out.println 不是日志,它没有级别、没有格式、没有归档策略。用正规的日志框架,比如 Logback、Log4j2,这是基本底线。
  • 日志量要可控。 线上环境 INFO 就够了,别开 DEBUG。我见过一个团队把 TRACE 开到线上,结果一天的日志量是正常情况的 50 倍,ELK 集群直接被打挂了。
  • 格式统一是团队的事。 一个项目的日志格式应该写在规范文档里,而不是让每个开发自己决定。不然你后面做日志采集的时候,光写解析规则就够你喝一壶的。
  • 别忘了日志归档和清理。 日志不清理,磁盘总会满的。按天滚动、保留 30 天或 90 天,这个策略在项目启动时就要定好。

最后说一句: 日志系统看似基础,但它是整个可观测性的基石。你想想看,监控告警、链路追踪、审计分析,哪一样离得开日志?这一章我们把概念对齐了,后面讲采集配置和性能优化的时候,你就能更快上手了。