日志系统概述:结构、级别与格式归纳总结

说起日志,很多新手觉得不就是打印点信息出来嘛。其实没那么简单。一个设计良好的日志系统,就像是飞机的黑匣子。出事之后,它是你还原现场的唯一依据。我在排查线上故障时,百分之八十的时间都在跟日志打交道。如果日志本身一团糟,那排查效率就低得吓人。

这一章,我们就把日志系统的几个核心概念掰开揉碎。搞懂结构、级别和格式,你写出来的日志才算“可用的日志”。

核心观点: 日志的价值不在于“记录了”,而在于“能看懂、能搜索、能关联”。

日志系统知识框架 日志系统核心三要素 ① 日志结构 ② 日志级别 ③ 日志格式 时间戳 线程/进程ID 模块/类名 消息体 DEBUG INFO WARN ERROR FATAL 纯文本 JSON 结构化 二进制 好的日志结构 = 快速定位问题 + 自动化分析 + 可追溯

一、日志结构:一条完整的日志长什么样?

一条日志,说白了就是一条包含上下文信息的记录。我见过很多团队,日志就写个字符串,比如“登录失败”。这种日志等于白写。出了事你根本不知道是哪个用户、什么时候、在哪台机器上登录失败。

一条高质量的日志,至少应该包含以下几部分:

字段 说明 典型示例
时间戳 精确到毫秒,包含时区 2025-03-18 14:23:45.678 +0800
日志级别 标记严重程度 ERROR
线程/进程ID 区分并发请求 thread-12
模块/类名 标明来源 com.payment.PaymentService
请求追踪ID 关联上下游调用链路 traceId=abc123def456
消息体 具体的事件描述 用户 9527 支付失败,余额不足

小建议: 我个人习惯在每个日志里都带上 traceId。这样哪怕服务跨了十个节点,也能把一次请求的日志串起来看。

再来看一个反面例子。你写出来的代码,别出现这种日志:

// ❌ 反面教材
log.info("连接超时");

// ✅ 正确示范
log.info("[DBConnectionPool] [pool-1] [traceId=xyz789] 连接超时,超时时间=5000ms,目标地址=jdbc:mysql://10.0.0.1:3306/orders");

二、日志级别:用对级别,别让ERROR变废话

日志级别可能是被用得最乱的东西。很多团队什么异常都打ERROR,结果就是ERROR日志一天上百万条,没人看。

我记得在维护一个老系统时,发现有人用 logger.error("用户登录") 来记录正常登录。我当时就愣住了——这到底是成功还是失败?

级别定义了事件的严重程度和紧急程度。一般分以下五级:

  1. DEBUG — 开发调试用。上线后直接关掉,别带到生产环境。
  2. INFO — 系统正常运行的关键节点。比如“订单创建成功”、“支付回调已接收”。
  3. WARN — 出问题了但系统能扛住。比如“重试第3次”、“缓存未命中”。
  4. ERROR — 功能受损,但系统还活着。比如“支付接口返回500”、“数据库写入失败”。
  5. FATAL — 系统要挂了。比如“内存溢出”、“主从同步断开”。遇到FATAL你就该爬起来看日志了。

避坑指南: 我曾经遇到一个项目,整个代码里只有两种级别:INFO和ERROR。DEBUG和WARN压根没人用。排查问题时,INFO日志不完整,ERROR日志又全是噪音。最后重构日志系统花了整整两周时间。所以一开始就要定好级别使用规范。

关于级别的配置,生产环境常见的做法是这样:

// 生产环境一般只开 WARN 及以上
rootLogger.level = WARN

// 特定模块可以单独开级别
logger.com.payment.level = INFO
logger.com.debug.feature.level = DEBUG

// 日志输出位置也要分级别
appender.file.error = /var/log/app/error.log   // 只写 ERROR + FATAL
appender.file.all = /var/log/app/app.log       // 写 WARN 及以上

三、日志格式:机器能读懂的日志才是好日志

很多人写日志只考虑人能不能看懂,忽略了机器能不能解析。现在日志量早就不是人工翻文件能处理的了。你得上ELK、得上Splunk、得上日志平台。如果你的日志格式乱七八糟,这些工具根本没法用。

日志格式可以分成三类:

格式类型 优点 缺点 适用场景
纯文本 人类易读,开发简单 机器解析困难,搜索效率低 小型服务,临时调试
JSON 结构化,机器友好,扩展性强 占用空间稍大,早期加载较慢 微服务、云原生、日志平台接入
二进制 空间小、性能极高 不可读,需要专用工具 嵌入式系统、高性能中间件

我推荐的做法: 新的微服务项目,别想太多,直接用JSON格式。为什么?因为日志平台(如ELK)原生支持JSON解析。你只要把字段名定好,Kibana里直接就能搜、能聚合、能画图。

JSON格式的日志长这样:

{
  "timestamp": "2025-03-18T14:23:45.678+0800",
  "level": "WARN",
  "thread": "http-nio-8080-exec-3",
  "logger": "com.gateway.RateLimiter",
  "traceId": "abc123def456",
  "message": "接口 /api/v1/orders 触发限流",
  "context": {
    "currentQPS": 350,
    "limitQPS": 300,
    "clientIP": "192.168.1.100"
  }
}

总结一下: 日志的级别、结构和格式是一个整体。级别帮你筛掉噪音,结构帮你找到关联,格式帮你喂给机器。三者配合好了,你才能从海量数据里快速定位问题。

顺便提一句,如果你的日志是纯文本格式,那也没关系。但建议你用统一的模式,比如 [级别] [模块] [traceId] 消息内容。这样至少正则表达式还能匹配得上。

嗯,日志系统的基础概念就先聊到这儿。这些知识点看着简单,但真要落地到项目里,每一步都有坑。后面几章我们会对每个部分展开深入讲,包括怎么设计日志策略、怎么避免性能损耗等等。