4. 日志收集核心概念:日志格式、级别与策略

聊到日志收集,很多人第一反应就是「把日志存起来」。但说实话,如果一开始没把格式和级别规划好,后面排查问题会非常痛苦。我在好几个项目里都见过这种场景——日志倒是全量采集了,但一查问题,全是 INFO 级别的废话,真正的 ERROR 被淹没了。

这一节,我们就把日志的四个核心概念掰开揉碎讲清楚。嗯,都是基本功,但也是决定你日志系统好不好用的关键。

4.1 日志格式:JSON vs 文本

日志格式说白了就两种:纯文本和结构化(比如 JSON)。

纯文本日志长这样:

2025-06-15 10:23:45.678 ERROR [http-nio-8080-exec-3] com.example.OrderService - 订单 12345 处理失败:库存不足

这种格式人类可读性很好,用 grep 一把梭也能查。但一旦接入 ELK 或 Loki 这类系统,解析起来就费劲了——你得写正则表达式去提取时间、级别、类名、消息。

JSON 日志长这样:

{
  "timestamp": "2025-06-15T10:23:45.678Z",
  "level": "ERROR",
  "logger": "com.example.OrderService",
  "thread": "http-nio-8080-exec-3",
  "message": "订单 12345 处理失败:库存不足",
  "orderId": "12345",
  "durationMs": 234
}

我个人习惯在生产环境强制使用 JSON 格式。为什么?因为字段是自描述的,采集端不需要写解析规则,直接就能把 orderIddurationMs 这些字段提取出来做聚合分析。

我的建议:开发环境可以用纯文本方便看,但生产环境一定要用 JSON。你想想看,如果日志里带了请求 ID、用户 ID,用 JSON 存下来,查问题时直接按 ID 过滤,效率翻倍。

4.2 日志级别:DEBUG、INFO、WARN、ERROR

日志级别是个老生常谈的话题,但我在项目中见过太多「级别滥用」的情况。说白了,级别就是用来控制日志输出量的开关。

级别 含义 生产环境建议
DEBUG 调试信息,开发用 关闭,除非排查特定问题
INFO 正常流程信息 开启,但别滥用
WARN 潜在问题,不影响运行 开启,用于预警
ERROR 错误,需要人工介入 必须开启,触发告警

我曾经接手过一个项目,代码里到处都是 logger.info("进入方法")logger.info("退出方法")。结果一天产生了几十 GB 的日志,真正有用的 ERROR 信息反而找不到了。这就是典型的级别滥用。

我的原则很简单:

  • DEBUG:只在你开发调试时用,上线前检查一遍,该删的删,该降级的降级。
  • INFO:记录关键业务流程的起止,比如「订单创建成功」「支付回调到达」。别把循环里的每次迭代都打 INFO。
  • WARN:记录那些「虽然没报错,但不太对劲」的情况。比如重试了 3 次才成功,或者配置项使用了默认值。
  • ERROR:必须记录异常堆栈和上下文信息。我习惯在 ERROR 日志里带上请求 ID 和关键参数,方便复现。
注意:千万别在循环里打 DEBUG 日志!我曾经见过一个同事在 for 循环里打了 10 万条 DEBUG 日志,直接把磁盘写满了。生产环境要动态调整日志级别,一般通过配置中心或 API 实现。

4.3 结构化日志 vs 非结构化日志

结构化日志,说白了就是「机器能直接解析的日志」。JSON 是典型代表,但也不限于 JSON——比如 Logstash 格式、GELF 格式也算。

非结构化日志就是纯文本,靠正则或者人工去看。

我为什么强调结构化?给你看个对比:

非结构化日志(文本):

2025-06-15 10:23:45 ERROR 订单 12345 处理失败,耗时 234ms

结构化日志(JSON):

{
  "level": "ERROR",
  "message": "订单处理失败",
  "orderId": "12345",
  "durationMs": 234,
  "service": "order-service",
  "traceId": "abc123"
}

你看,结构化日志里每个字段都是独立的。在 Kibana 里,你可以直接按 orderId 搜索,按 durationMs 排序,甚至做聚合统计——「今天所有耗时超过 1 秒的订单有哪些?」。非结构化日志要做到这些,你得先写正则把字段抠出来。

我在项目中遇到过一个典型案例:某次线上故障,需要查某个用户的所有操作记录。因为日志里带了 userId 字段,我在 Elasticsearch 里一秒就过滤出来了。如果是纯文本,你得 grep 半天,还容易漏。

核心结论:结构化日志是云原生时代的标配。不管你用 ELK、Loki 还是 ClickHouse,结构化数据都能让查询和分析效率提升一个数量级。

4.4 日志轮转与保留策略

日志如果不轮转,磁盘迟早会被写满。这可不是危言耸听——我见过不止一次因为日志把根目录写满,导致服务直接挂掉的惨案。

日志轮转就是按大小或时间切分日志文件。比如:

  • 按大小:每个日志文件 100MB,满了就新建一个
  • 按时间:每小时或每天生成一个新文件
  • 按数量:只保留最近 7 个文件,旧的自动删除

常见的轮转配置(以 Logback 为例):

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

这个配置的意思是:每天生成一个日志文件,保留最近 30 天。

保留策略要考虑几个因素:

  • 合规要求:金融行业可能要求保留 180 天甚至更久
  • 存储成本:日志量大的话,保留 30 天可能就要几 TB
  • 查询需求:最近 7 天的日志最常查,可以放热存储;更早的放冷存储或归档
我的经验:不要把日志轮转和采集混为一谈。轮转是本地文件管理,采集是把日志发送到中心化存储。我习惯在采集端(Filebeat/Fluentd)配置一个「采集完成再删除」的策略,避免日志还没采集就被轮转删掉了。

嗯,到这里日志的核心概念就讲完了。说白了,格式决定了解析成本,级别控制了日志量,结构化让查询变得简单,轮转和保留策略则保证了系统的稳定性。下一节我们开始实战——怎么用 Filebeat 把日志采集到 Elasticsearch。