第2章:日志系统基础

日志这东西,说白了就是系统的「黑匣子」。我刚开始做运维那会儿,总觉得日志就是出问题了才去看一眼。后来被线上故障狠狠教育过几次,才明白——日志系统设计得好不好,直接决定了你排查故障的速度。

这一章,咱们就聊聊日志系统的三个核心问题:日志该怎么分级、格式怎么写才规范、以及怎么把分散的日志集中起来管理。

2.1 日志级别:DEBUG / INFO / WARN / ERROR

日志级别不是随便定的。每个级别都有它明确的用途。我个人习惯把这四个级别分成两类:调试用监控用

级别 含义 生产环境建议 典型场景
DEBUG 调试信息 关闭 变量值、函数调用栈、SQL语句
INFO 正常流程信息 开启 服务启动、请求到达、任务完成
WARN 潜在问题 开启 重试、降级、配置异常
ERROR 错误/异常 开启 数据库连接失败、接口超时、业务异常

核心原则:生产环境只开 INFO 及以上级别。DEBUG 日志量太大,会拖垮磁盘 IO。我见过一个团队把 DEBUG 开在生产环境,结果一天写了 200GB 日志,直接把磁盘打满了。

这里有个坑,我踩过好几次——WARN 和 ERROR 的边界。很多人把「重试」也打成了 ERROR,结果告警满天飞。我的经验是:影响用户操作的,打 ERROR;不影响用户操作的,打 WARN。比如接口第一次调用超时,自动重试成功,这就是 WARN。重试三次都失败,那才是 ERROR。

小技巧:在代码里统一封装一个日志工具类,强制要求调用时传入「业务场景」参数。这样后期做日志分析时,能快速定位到某个业务模块的日志。

2.2 日志格式规范

日志格式不统一,排查故障时你会想骂人。我接手过一个老项目,有的日志用逗号分隔,有的用竖线,有的干脆是自由文本。那次排查一个支付超时问题,我花了三个小时才把日志拼起来。

所以,我强烈建议团队统一使用 JSON 格式。为什么?因为 JSON 结构清晰,解析方便,ELK 和 Loki 都能直接识别。

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

  • timestamp:时间戳,精确到毫秒,带时区
  • level:日志级别
  • logger:类名或模块名
  • message:日志正文
  • trace_id:链路追踪 ID(这个很重要,后面会细讲)
  • thread:线程名(Java 环境)

举个例子,这是我常用的日志格式:

{
  "timestamp": "2025-03-21T14:30:22.123+08:00",
  "level": "ERROR",
  "logger": "com.example.order.OrderService",
  "message": "订单支付失败,订单号: ORD20250321001",
  "trace_id": "a1b2c3d4e5f6",
  "thread": "http-nio-8080-exec-3",
  "extra": {
    "order_id": "ORD20250321001",
    "amount": 299.00,
    "error_code": "PAY_TIMEOUT"
  }
}

注意:千万不要把敏感信息写进日志!比如用户密码、身份证号、银行卡号。我曾经见过一个同事把完整的用户信息打到了日志里,结果被安全部门通报了。建议在日志输出前做脱敏处理。

另外,日志里一定要带 trace_id。你想想看,一个请求经过网关、订单服务、支付服务、通知服务,如果没有 trace_id,你怎么把这一串日志串起来?有了 trace_id,在 ELK 里一搜,整个请求的完整链路就出来了。

2.3 集中式日志收集:ELK 与 Loki

日志分散在各台机器上,排查故障时你得一台一台登录去看。这效率,说实话太低了。集中式日志收集就是为了解决这个问题。

目前主流的方案有两个:ELKLoki。我两个都用过,说说我的感受。

2.3.1 ELK 栈

ELK 是 Elasticsearch + Logstash + Kibana 的简称。它的工作流程是这样的:

  1. Logstash 从各台机器采集日志
  2. 对日志进行解析、过滤、格式化
  3. 写入 Elasticsearch 做索引和存储
  4. 通过 Kibana 做可视化和搜索

ELK 的优点很明显:功能强大,搜索速度快,支持复杂的聚合分析。但缺点也很突出——太重了。Elasticsearch 本身就很吃内存,Logstash 也消耗资源。小团队用 ELK,光维护成本就够呛。

我记得有一次帮一个创业公司搭 ELK,他们总共就 5 台服务器,结果 ELK 集群占了 3 台。老板看了直摇头。

2.3.2 Loki

Loki 是 Grafana 团队推出的日志系统。它的设计理念和 ELK 完全不同——不索引日志内容,只索引元数据。说白了,它把日志当成普通的文本文件来存,只给日志打上标签(比如服务名、主机名、级别)。

这样做的好处是:省资源。Loki 的存储成本只有 ELK 的十分之一左右。而且它和 Prometheus、Grafana 天然集成,你可以在同一个 Grafana 面板上同时看指标和日志。

但 Loki 也有短板:全文搜索能力弱。如果你想搜「订单号 ORD20250321001 在哪些日志里出现过」,Loki 的响应速度会比 ELK 慢不少。

对比项 ELK Loki
资源消耗
搜索速度 快(全文索引) 中等(元数据索引)
存储成本
与 Prometheus 集成 一般 原生支持
适用场景 大型企业、复杂查询 中小团队、Kubernetes 环境

我的建议:如果你团队规模不大(10 台服务器以内),或者主要跑在 Kubernetes 上,优先选 Loki。如果你需要做复杂的日志分析、安全审计,或者团队有专职的 ES 运维,那就选 ELK。

2.4 实战:日志采集配置示例

光说不练假把式。我给大家看一个 Loki 的采集配置,用的是 Promtail(Loki 的日志采集器)。

# promtail.yaml
scrape_configs:
  - job_name: 'application-logs'
    static_configs:
      - targets: ['localhost']
        labels:
          job: 'order-service'
          __path__: /var/log/order-service/*.log
    pipeline_stages:
      - json:
          expressions:
            level: level
            trace_id: trace_id
            message: message
      - labels:
          level:
          trace_id:

这个配置做了三件事:

  • 采集 /var/log/order-service/ 目录下所有 .log 文件
  • 给日志打上 job=order-service 的标签
  • 从 JSON 日志中提取 leveltrace_id 作为额外的标签

配置好之后,在 Grafana 里就能直接搜了。比如我想看 order-service 最近一小时的 ERROR 日志,直接在查询框里输入:

{job="order-service"} |= "ERROR"

嗯,就是这么简单。

避坑指南:我曾经遇到过一个问题——日志采集器把日志文件删了。原因是 Promtail 默认会清理已经采集过的日志文件。如果你需要保留原始日志,记得在配置里加上 keep_file: true

2.5 本章小结

日志系统是故障排查的基石。这一章我们聊了三个要点:

  • 日志级别:DEBUG 用于开发,INFO 用于记录流程,WARN 用于预警,ERROR 用于告警。别混用。
  • 日志格式:统一用 JSON,带上 timestamp、level、trace_id 等关键字段。敏感信息要脱敏。
  • 集中式收集:ELK 功能强但资源消耗大,Loki 轻量但搜索能力弱。根据团队规模选型。

下一章,咱们会深入聊 链路追踪。到时候你会看到,trace_id 这个小小的字段,在分布式系统里能发挥多大的作用。