3、日志采集基础:日志格式规范、日志级别与切割策略

日志这东西,说白了就是系统的「黑匣子」。你想想看,线上出了故障,第一反应是不是先翻日志?我做了这么多年运维,见过太多团队因为日志不规范,排查问题像大海捞针。今天咱们就把日志的底子打扎实。

3.1 日志格式规范:JSON 还是纯文本?

这是个老生常谈的问题。我个人习惯,生产环境一律用 JSON 格式。为什么?因为机器好解析。

纯文本日志长这样:

2025-01-15 10:23:45 ERROR 用户下单失败,订单号: ORD20250115001,原因: 库存不足

看着挺清楚对吧?但你要用 Logstash 或者 Fluentd 去解析它,就得写正则表达式。字段一多,正则就变成天书了。我在项目中遇到过,一个正则写错,整条日志解析失败,数据全丢了。

JSON 格式就好很多:

{
  "timestamp": "2025-01-15T10:23:45.123+08:00",
  "level": "ERROR",
  "logger": "com.example.OrderService",
  "thread": "http-nio-8080-exec-3",
  "message": "用户下单失败",
  "orderId": "ORD20250115001",
  "reason": "库存不足",
  "userId": 10086,
  "duration": 235
}

你看,字段名、字段类型一目了然。采集端拿到 JSON,直接按 key 取值就行,省心省力。

核心原则:日志格式要「机器可读优先,人类可读其次」。JSON 是当前最通用的选择。

3.2 日志字段设计:该记什么?

不是所有信息都往日志里塞。我见过有人把整个 HTTP 请求体都打出来,结果一天日志量几个 TB,磁盘直接爆了。嗯,这里要注意,日志字段要精打细算。

我建议至少包含以下字段:

字段 类型 说明 是否必填
timestamp ISO8601 字符串 日志产生时间,带时区 是
level 字符串 日志级别:DEBUG/INFO/WARN/ERROR 是
logger 字符串 产生日志的类名或模块名 是
thread 字符串 线程名,方便追踪并发问题 推荐
message 字符串 日志正文,简明扼要 是
traceId 字符串 分布式链路追踪 ID 推荐
context 对象 业务上下文,如 userId、orderId 按需
duration 整数(ms) 接口或操作耗时 按需
小技巧:timestamp 一定要用 ISO8601 格式带时区,比如 2025-01-15T10:23:45.123+08:00。别用时间戳毫秒数,排查问题时谁去换算?

3.3 日志级别:别什么都打 ERROR

日志级别是个老话题,但很多人用不好。我见过最离谱的,有人把「用户查询商品列表」这种正常操作也打 INFO,一天下来 INFO 日志几千万条,真正有用的 ERROR 反而被淹没了。

我的建议是这样的:

  • FATAL:系统即将崩溃,比如数据库连接池耗尽、磁盘空间不足。这种级别应该触发告警,直接打电话给值班人员。
  • ERROR:请求处理失败,但系统还能继续运行。比如用户下单失败、第三方接口超时。需要人工介入排查。
  • WARN:潜在问题,但当前请求还能正常返回。比如配置项缺失使用默认值、接口响应时间超过阈值。
  • INFO:关键业务流程节点。比如用户注册成功、订单状态变更。别把循环里的每次迭代都打 INFO。
  • DEBUG:开发阶段调试用。线上环境建议关闭,或者按需动态开启。
  • TRACE:比 DEBUG 更细的粒度,一般只在定位疑难问题时临时开启。
避坑指南:我曾经在一个项目中,同事把数据库查询异常打成了 WARN 级别。结果线上数据库挂了,告警系统没触发,等我们发现时已经过去半小时。记住:凡是需要人工处理的,至少打 ERROR。

3.4 日志切割策略:别让日志撑爆磁盘

日志如果不切割,一天下来一个文件几个 GB,打开都费劲。更别说采集了。切割策略说白了就两个维度:按时间和按大小。

常见的切割方案:

策略 配置示例 适用场景
按天切割 每天 00:00 生成新文件 日志量稳定,适合长期归档
按小时切割 每小时生成一个新文件 日志量大,需要快速定位
按大小切割 单个文件超过 100MB 就切 日志量波动大,防止单文件过大
混合策略 按小时 + 最大 500MB 生产环境推荐,双重保险

以 Java 的 Logback 为例,配置一个混合策略:

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  <file>/var/log/app/app.log</file>
  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    <fileNamePattern>/var/log/app/app.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
    <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
      <maxFileSize>100MB</maxFileSize>
    </timeBasedFileNamingAndTriggeringPolicy>
    <maxHistory>30</maxHistory>
  </rollingPolicy>
</appender>

这个配置的意思是:按天切割,但如果当天日志超过 100MB,就再切一个文件。保留最近 30 天的日志。

经验之谈:切割后的日志文件名一定要带时间戳。我见过有人用 app.log.1、app.log.2 这种命名,结果采集器不知道哪个是最新的,乱套了。

3.5 知识体系总览

下面这张图把日志采集基础的核心知识点串起来了,你可以对照着梳理自己的日志体系:

日志采集基础知识体系 日志采集基础 日志格式规范 日志级别 日志切割策略 JSON 格式 纯文本格式 FATAL ERROR 按时间切割 按大小切割 核心目标:让日志「机器可读、人可查、磁盘不爆」 三者缺一不可,否则日志系统就是摆设

3.6 实战建议:从零开始怎么搭?

如果你现在要从零开始,我建议按这个顺序来:

  1. 先定格式:统一用 JSON,定义好字段模板。团队内部拉个会,把字段名对齐。
  2. 再定级别:明确什么情况打什么级别。最好写进开发规范里,代码 Review 时重点检查。
  3. 最后配切割:根据预估日志量,选好切割策略。初期可以按天 + 按大小混合,后期再调。
一句话总结:日志格式要统一、级别要分明、切割要自动。这三件事做好了,后面的采集、存储、分析才能顺起来。