第4章 日志管理:日志规范设计、日志轮转策略、集中式日志收集(ELK/Loki)

日志这东西,平时没人搭理它,一出事它就是唯一的救命稻草。我在做市系统里摸爬滚打这些年,见过太多因为日志没管好,排查问题花了一整夜的惨案。说白了,日志管理不是写写代码就完事,它是一套从规范到存储再到检索的完整体系。

4.1 日志规范设计:别让日志变成噪音

我刚开始带团队时,大家写日志全凭心情。有人用中文,有人用英文,有人干脆不写。结果呢?系统崩了,日志里全是“error”、“failed”,连个上下文都没有。嗯,这里要注意,日志规范必须从第一天就定死。

4.1.1 日志分级:什么级别该打什么日志

我个人习惯用这五个级别,少一个都不行:

级别 含义 使用场景 示例
FATAL 致命错误 系统无法继续运行,必须人工介入 数据库连接池耗尽、内存溢出
ERROR 错误 业务逻辑异常,但系统还能跑 订单撮合失败、风控校验不通过
WARN 警告 潜在风险,不影响当前功能 网络延迟超过阈值、重试次数过多
INFO 信息 关键业务流程节点 订单提交成功、持仓更新完成
DEBUG 调试 开发阶段或线上临时排查 变量值、函数调用栈
避坑指南: 我曾经见过一个团队,把DEBUG日志直接打到生产环境,一天产生200GB日志,磁盘直接爆了。记住,生产环境默认只开INFO及以上级别,DEBUG必须通过开关动态开启。

4.1.2 日志格式:统一模板,机器可读

你想想看,如果每行日志格式都不一样,ELK怎么解析?正则写到崩溃。我建议用JSON格式,结构清晰,解析方便。

{
  "timestamp": "2025-03-15T10:30:00.123+08:00",
  "level": "INFO",
  "logger": "com.mm.order.OrderService",
  "thread": "order-pool-7",
  "message": "订单提交成功",
  "context": {
    "order_id": "ORD202503150001",
    "symbol": "BTC/USDT",
    "side": "BUY",
    "price": 65000.12,
    "quantity": 0.5
  },
  "trace_id": "a1b2c3d4e5f6"
}

这里面有个关键字段——trace_id。做市系统里一个订单可能经过网关、风控、撮合、清算好几个服务,没有trace_id,你根本串不起来。我个人习惯用OpenTelemetry自动生成,省心。

4.2 日志轮转策略:别让日志撑爆磁盘

我记得刚入行时,有一次凌晨三点被电话叫醒,说系统挂了。上去一看,磁盘100%,日志文件已经50GB了。从那以后,日志轮转成了我每次部署必检查的项目。

4.2.1 基于大小的轮转

最常用的策略。每个日志文件达到指定大小就切分,保留最近N个文件。

# logback.xml 配置示例
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
  <file>/var/log/mm/mm.log</file>
  <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
    <fileNamePattern>/var/log/mm/mm.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
    <maxFileSize>500MB</maxFileSize>
    <maxHistory>30</maxHistory>
    <totalSizeCap>20GB</totalSizeCap>
  </rollingPolicy>
</appender>
我的经验: 做市系统日志量很大,建议单文件控制在500MB以内。maxHistory设30天,totalSizeCap设20GB,这样既保证能回溯一个月,又不会撑爆磁盘。

4.2.2 基于时间的轮转

按天或按小时切分。适合日志量比较稳定的场景。我个人更推荐“大小+时间”组合策略,双保险。

4.2.3 压缩与清理

轮转后的旧日志,建议立即压缩。gzip压缩率通常在10:1左右,能省不少空间。同时要设置自动清理策略,别让压缩文件也堆成山。

# 压缩策略示例
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
  <maxFileSize>500MB</maxFileSize>
</triggeringPolicy>
<!-- 压缩旧日志 -->
<encoder>
  <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>

4.3 集中式日志收集:ELK vs Loki

单机看日志还行,一旦服务超过3个,你就得用集中式方案了。目前主流就两套:ELK和Loki。我两个都用过,各有千秋。

4.3.1 ELK:老牌劲旅,功能全面

ELK = Elasticsearch + Logstash + Kibana。这套组合拳打了十年,生态成熟,文档丰富。

  • Elasticsearch:存储+检索,全文搜索能力极强
  • Logstash:数据采集+解析,支持各种输入源
  • Kibana:可视化+告警,Dashboard做得漂亮

我在项目中遇到过一个问题:Logstash吃内存太狠,动不动就OOM。后来我换成Filebeat采集,Logstash只做解析,才稳住。

# Filebeat 配置示例
filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /var/log/mm/*.log
  json.keys_under_root: true
  json.overwrite_keys: true

output.logstash:
  hosts: ["logstash.mm.com:5044"]

4.3.2 Loki:轻量新秀,成本更低

Loki是Grafana家的产品,设计理念跟ELK完全不同。它不全文索引,只索引标签,存储成本低得多。

  • Loki:存储,基于标签索引,压缩率高
  • Promtail:采集端,类似Filebeat
  • Grafana:可视化,跟监控面板统一
选型建议: 如果你团队已经有Grafana做监控,强烈建议上Loki。一套Grafana既能看指标又能看日志,运维成本直线下降。ELK适合需要复杂全文搜索的场景,比如安全审计、慢查询分析。

4.3.3 架构对比

我画了一张图,帮你快速理解两者的核心差异:

ELK 架构 应用服务 Filebeat Logstash Elasticsearch Kibana Loki 架构 应用服务 Promtail Loki Grafana ELK:功能全面,适合复杂检索 | Loki:轻量高效,适合与监控统一

4.4 实战:搭建一套日志收集流水线

光说不练假把式。我拿Loki举例,因为它在做市系统里更实用——你想想看,做市系统最怕的就是延迟,Loki资源占用低,不会跟交易进程抢CPU。

4.4.1 部署Promtail

每台机器上部署一个Promtail,采集日志并打上标签。

# promtail.yml
scrape_configs:
- job_name: mm-system
  static_configs:
  - targets: [localhost]
    labels:
      job: mm
      env: production
      __path__: /var/log/mm/*.log
  pipeline_stages:
  - json:
      expressions:
        level: level
        message: message
        trace_id: trace_id

4.4.2 配置Loki存储

Loki支持本地存储和对象存储。生产环境我建议用S3或MinIO,成本低,扩容方便。

# loki.yml
storage_config:
  boltdb_shipper:
    active_index_directory: /data/loki/index
    cache_location: /data/loki/cache
    shared_store: s3
  aws:
    s3: s3://bucket-name/loki/
    region: us-east-1

4.4.3 在Grafana中查询

部署完成后,在Grafana里添加Loki数据源,就能用LogQL查询了。

# 查询某个trace_id的所有日志
{job="mm", env="production"} |= "trace_id=a1b2c3d4e5f6"

# 查询最近1小时ERROR级别的日志
{job="mm"} |= "ERROR" | logfmt | __timestamp__ > now() - 1h
我的小技巧: 在Grafana里把日志查询和监控面板放在同一个Dashboard上。比如,当CPU飙升时,旁边就是日志面板,直接按时间戳过滤,一秒定位问题。

4.5 避坑指南:日志管理常见问题

  • 日志打太多:我曾经见过一个开发在循环里打DEBUG日志,每秒输出10万行。记住,循环里别打日志,真要打就用采样。
  • 时区不统一:所有服务强制使用UTC时间,前端展示时再转本地时间。否则跨时区排查问题,你会疯掉。
  • 敏感信息泄露:日志里别打密码、密钥、用户手机号。我见过有人把数据库密码直接打在ERROR日志里,然后日志被ELK索引了...后果你懂的。
  • 日志采集延迟:Promtail默认10秒采集一次,如果系统出问题,这10秒可能就是生死线。建议改成1秒。

日志管理这件事,前期多花一小时设计规范,后期能省一百小时排查问题。别等到磁盘爆了、问题查不到了,才想起日志的重要性。

专注资料整理