Files
cs-note/hhs/MS/04-可观测性/02-日志系统.md
T
2026-05-24 11:42:38 +08:00

5.1 KiB

tags, create time
tags create time
microservice
logging
elk
loki
structured-logging
2026-05-05

日志系统

概述

Logging 回答的问题是:具体发生了什么?——通过原始事件记录提供上下文。

在微服务架构中,所有服务的日志必须集中收集。散落在各台机器上的日志无法支撑有效的故障排查。

flowchart LR
    APP[应用容器] -->|stdout/stderr| COLLECTOR[采集器<br/>FLUENTD / Filebeat]
    COLLECTOR --> PARSE["解析 & 过滤"]
    PARSE --> ES[(Elasticsearch)]
    PARSE --> LOKI[(Loki)]
    ES --> GRAF["Grafana / Kibana"]
    LOKI --> GRAF
    
    style COLLECTOR fill:#fff3e0
    style PARSE fill:#e3f2fd

结构化日志

不要写纯文本日志。推荐 JSON 格式,便于程序解析和查询:

{
  "timestamp": "2026-05-05T10:30:00Z",
  "level": "ERROR",
  "service": "order-service",
  "trace_id": "abc-123-def",
  "span_id": "span-456",
  "msg": "failed to connect payment service",
  "caller": "payment/client.go:42",
  "cost_ms": 30000,
  "user_id": "user-42"
}

最小字段集合(必选)

字段 格式 说明
timestamp ISO 8601 (2026-05-05T10:30:00Z) UTC 时区,不可省略
level TRACE / DEBUG / INFO / WARN / ERROR / FATAL 大小写统一
service 字符串 微服务名称
trace_id 字符串 链路追踪 ID,与 Tracing 串联
msg 字符串 人类可读的消息描述

日志级别使用规范

级别 使用场景 示例
TRACE 调试级详细输出(仅开发环境) 方法入参/出参、循环体内部状态
DEBUG 诊断信息 缓存命中率、连接池状态
INFO 关键业务事件 下单成功、支付回调、用户注册
WARN 异常但不致命 重试了一次、走降级、配置热更新
ERROR 影响单个请求的失败 DB 超时、下游调用失败、空指针
FATAL 进程无法继续运行 配置文件缺失、依赖服务完全不可用

[!warning] 最佳实践

  • 不要记录敏感信息:密码、Token、身份证号等绝不能出现在日志中
  • INFO 级记录关键业务事件——这些是排查业务问题的主线
  • WARN 级记录异常但不致命——如重试、降级
  • ERROR 级必须有 trace_id 和错误堆栈,否则毫无价值

日志采集管线

flowchart LR
    App["业务应用"] -->|stdout/stderr| K8sPod["Pod 容器日志"]
    K8sPod --> Collector["采集器<br/>Filebeat / FluentBit"]
    Collector --> Backend["存储后端<br/>Elasticsearch / Loki"]
    Backend --> UI["查询面板<br/>Grafana / Kibana"]
    
    Collector -->|"过滤掉 health check"| Drop["丢弃无用日志"]
    
    style Drop fill:#ffebee,stroke:#ef5350

采集器选型

组件 语言 资源消耗 特点
Filebeat (ELK) Go 低 轻量,适合 K8s DaemonSet
Fluentd Ruby 中 插件生态丰富,处理灵活
FluentBit C 极低 边缘场景首选,K8s 官方推荐

日志方案对比

方案 存储 查询能力 成本 适用场景
ELK (Elasticsearch) ES 集群 全文检索强大 高 日志量大、需要复杂分析
Loki + Grafana 对象存储 (S3) Label 索引 + LogQL 低 已在用 Grafana 的团队
云 SLS / CloudWatch 云托管 开箱即用 中~高 全云原生环境

Loki vs ELK 决策树

flowchart TD
    Q{"需要全文检索吗?"}
    
    Q -- "否,按时间/Service/TraceId 查询" --> LOKI["✅ Loki<br/>低成本、轻量"]
    Q -- "是,复杂的全文搜索" --> ELK["✅ ELK<br/>功能强大"]
    
    LOKI --> Check{"日日志量 > 1TB?"}
    Check -- 是 --> ConsiderES["考虑混合架构<br/>高频用 Loki,低频归档到 ES"]
    Check -- 否 --> Done["直接用 Loki ✅"]
    
    ELK --> Check2{"团队有经验?"}
    Check2 -- 是 --> UseES["直接上 ES ✅"]
    Check2 -- 否 --> Managed["用托管版 (阿里云 SLS)"]

采样策略

生产环境中并非所有日志都要采集。正常路径采 1%,异常路径全量。

正常 HTTP 200 → 1% 采样率        → 每小时 ~360 条日志
异常 HTTP 5xx → 100% 全量采集     → 可能数千条日志
DB 慢查询     → 100% 全量采集     → 重点关注
健康检查      → 不采集            → 零成本

[!tip] 实现方式 采样逻辑建议写在日志库的中间件层,而非业务代码里:

func SampleLogger(next logger.Logger, rate float64) logger.Logger {
    return &sampledLogger{
        next: next,
        shouldSample: func() bool {
            return rand.Float64() < rate
        },
    }
}

关联笔记