设计日志与监控系统

本文深度设计一个可观测性平台(日志 + 指标 + 追踪):覆盖日志采集 Agent 与管道、传输缓冲与削峰、存储选型(ES/Loki/时序库)、指标与告警、分布式追踪关联、高可用设计、采样与分层成本控制,以及查询与可视化,附采集配置、告警规则与时序存储 Schema。

日志与监控是每一个生产系统的「眼睛」。当业务系统出问题时,第一件事就是去看日志、看指标、看链路。它同时是「海量写入」和「高频查询」的典型矛盾体:每天上亿条日志要便宜地写进来,还要能在几秒内查出来。本文按面试答题结构设计一个集日志、指标、追踪于一体的可观测性平台。

一句话:可观测性系统的核心矛盾是「海量写入 vs 快速查询」,解法是采集端削峰、存储端分层、查询端倒排/时序索引,最后用告警把数据变成行动。

一、需求澄清与量级估算

1.1 需求澄清

  • 范围:只做日志,还是日志 + 指标 + 分布式追踪(完整可观测性)?我们做完整三件套。
  • 接入对象:自研微服务、第三方云服务、数据库/中间件?
  • 查询诉求:关键字检索、聚合统计、链路查看、告警规则?
  • 保留期:热数据保留多久,冷数据(合规)保留多久?
  • 规模与成本:千万级容器集群?日志量级多大?是否接受采样降成本?

明确假设:

需求项假设
服务数5000 个微服务实例
日志量峰值 10 亿条/天,约 30 TB/天
指标量5000 万 个时间序列(series)
追踪量每秒 20 万 span
查询 SLA关键字检索 P95 < 3s
告警分钟级规则判定,多通道通知

1.2 量级估算

指标估算值推导
日志写入 QPS~120 万/秒10 亿/86400 × 峰值系数
每日日志原始量30 TB每条约 3 KB × 10 亿
压缩后存储8 TB/天压缩比 3-4 倍
年存储~3 PB含冷存储分层
指标写入数百万/秒5000 万 series × 采集周期 15s

一句话:写带宽上百 GB/s、存储 PB 级,可观测系统「写」必须走管道削峰 + 批量压缩,绝不能逐个请求同步落库。

1.3 非功能需求

需求目标说明
可用性99.99%故障排查时它必须在线,反向依赖它
写入可靠性至少一次、尽量不丢采集端缓冲 + 管道重试
查询延迟P95 < 3s关键字检索
数据保留热 7 天 / 冷 1 年+合规与排障
安全合规脱敏、权限隔离、审计日志可能含 PII

一句话:可观测系统自身的可用性优先于一切——监控挂了,故障排查就瞎了,所以要格外强调冗余与缓冲。

二、高层架构设计

     微服务实例1     微服务实例2     ...   数据库/中间件/网关
         │              │                  │
         ▼              ▼                  ▼
   ┌─────────────────────────────────────────────┐
   │              采集层 (Agent / Exporter)        │
   │  日志采集Filebeat/Fluentd  指标采集Prometheus │
   │  追踪采集 OTel SDK + Agent 链路聚合            │
   └───────────────┬─────────────────────────────┘
                   │ (批量/压缩/本地缓冲)
   ┌───────────────▼─────────────────────────────┐
   │              传输与缓冲层                      │
   │  Kafka / 管道 (削峰、批量、分区、重试)         │
   └───────────────┬─────────────────────────────┘
   ┌───────────────▼─────────────────────────────┐
   │               存储与处理层                     │
   │  日志: ES/Loki  │ 指标: 时序库(Prometheus/TSDB) │
   │  追踪: 链路存储(Otel/ClickHouse)              │
   │  实时处理: Flink(解析/提取/聚合/告警计算)      │
   └───────────────┬─────────────────────────────┘
                   │
   ┌───────────────▼─────────────────────────────┐
   │              查询与应用层                      │
   │  搜索/看板(Grafana)  │ 告警/通知 │ 追踪查看 │
   └─────────────────────────────────────────────┘

2.1 三大数据类型对比

类型形态典型存储查询方式
日志(Logs)非结构化文本ES / Loki全文检索、正则、字段过滤
指标(Metrics)数值时间序列Prometheus TSDB / VictoriaMetrics聚合、下采样、同比环比
追踪(Traces)树状调用链Tempo / Jaeger / ClickHouse按 traceId 关联、瀑布图

一句话:日志回答「发生了什么」,指标回答「规模多大、是否健康」,追踪回答「哪一环拖慢」,三者在 traceId / 时间戳上关联成统一视图。

三、核心组件设计

3.1 日志采集 Agent

Agent 以 DaemonSet / Sidecar 部署在每个节点,职责:

  • 采集多源日志(stdout、文件、syslog),解析多行与格式。
  • 本地缓冲 + 批量发送:失败重试,防止源端抖动导致丢日志。
  • 标签注入:添加 service、pod、namespace、node、环境等维度字段。
filebeat.inputs:
  - type: container
    paths: ["/var/log/containers/*.log"]
    fields:
      service: payment
      env: prod
processors:
  - add_host_metadata: {}
  - dissect:
      tokenizer: "%{ts} [%{level}] %{msg}"
output.kafka:
  hosts: ["kafka:9092"]
  topic: "raw-logs"
  partition.hash:
    hash: ["fields.service"]

3.2 传输与缓冲层

  • Kafka:天然削峰(写入快、消费慢)、多消费者(存储 + Flink 实时处理)、可回溯。
  • 分区策略:按 service 分区,保证单服务日志有序(排查问题友好)。
  • 批量与压缩:Agent 端 gzip/lz4 压缩,Kafka 端批量写入,吞吐可提高 3-5 倍。

一句话:日志写入的高峰是突发性的,Kafka 做缓冲池把「写」与「处理」解耦,是海量日志系统的命脉。

3.3 存储选型

方案优势劣势适用
Elasticsearch全文检索强、聚合丰富写入成本高、集群运维重中小规模、复杂查询
Loki只存索引 + 对象存储,成本低全文能力弱、依赖标签大规模、成本敏感
ClickHouse列式、压缩比高、聚合极快全文检索需配合超大规模日志/链路
Prometheus/TSDB指标标准生态高基数痛点指标监控
VictoriaMetrics高基数优化、低成本社区相对小大规模指标

本文选型组合:日志走「Kafka → 实时解析 → ClickHouse(热)+ 对象存储(冷)」,查询层用 ClickHouse + 标签索引;指标走 VictoriaMetrics;追踪走「OTel → Kafka → ClickHouse 表」。

3.4 日志存储 Schema(ClickHouse)

CREATE TABLE logs ON CLUSTER cluster (
  timestamp   DateTime64(3),
  service     LowCardinality(String),
  level       LowCardinality(String),
  host        LowCardinality(String),
  pod         LowCardinality(String),
  trace_id    String,
  message     String,
  fields      Map(String, String)      -- 半结构化字段
) ENGINE = MergeTree()
PARTITION BY toYYYYMMDD(timestamp)
ORDER BY (service, timestamp, trace_id)   -- 分区裁剪 + 范围扫描
TTL toDateTime(timestamp) + INTERVAL 30 DAY TO VOLUME 'cold';

CREATE TABLE metrics ON CLUSTER cluster (
  ts          DateTime,
  metric      LowCardinality(String),   -- 指标名
  labels      Map(String, String),      -- 维度标签
  value       Float64
) ENGINE = MergeTree()
PARTITION BY toYYYYMMDD(ts)
ORDER BY (metric, ts, labels);

3.5 告警系统

指标/日志 → Flink/规则引擎 持续计算 → 命中阈值/异常 → 生成告警事件
  → 聚合去重(同一故障只告一次) → 分派路由 → 通知(PagerDuty/钉钉/邮件/IM)
  → 跟踪确认/恢复 → 降噪(静默/升级)

规则定义示例:

- name: 支付服务高错误率
  expr: rate(log_error_total{service="payment"}[5m]) / rate(log_total{service="payment"}[5m]) > 0.05
  for: 5m
  labels:
    severity: P1
  annotations:
    summary: 支付错误率超过 5%

告警设计要点:避免「告警风暴」(同一故障多条告警)——用分组 + 依赖树 + 静默期;告警必须可行动(给出 runbook 链接);用 SLO 指导告警优先级(先报用户可感知的)。

3.6 日志规范与上下文注入

日志系统的效果上限由「日志写得好不好」决定,采集端要推动三个规范:

  1. 结构化日志:不要拼字符串,用 JSON 结构化字段(level、ts、service、trace_id、业务字段),便于索引与过滤。
  2. 上下文注入:请求入口生成 request_id / trace_id,注入日志,让同一次请求的所有日志可一键串联。
  3. 日志分级:ERROR 必须含可定位信息(异常栈、关键参数);DEBUG 只进调试环境,生产默认 INFO 起;敏感信息脱敏(手机号、卡号打码)。
# 好的日志: 结构化 + 带 trace_id
logger.info(
    "order_paid",
    extra={
        "trace_id": req.trace_id,
        "order_id": order.id,
        "amount": order.amount_cents,
        "channel": order.channel,
    },
)
# 坏的日志: 拼字符串, 无上下文
logger.info(f"订单 {order.id} 支付成功, 金额 {order.amount}")

一句话:可观测性平台的「数据质量」由日志规范决定——结构化 + 关联 ID 是海量日志能被高效检索的前提。

四、关键流程

4.1 分布式追踪关联

用户请求 → 网关生成 traceId, 注入 header (X-Trace-Id)
  → A服务 生成 span(耗时) → B服务 子 span → C服务 ...
  → SDK 采样(头部采样/尾采样) → 上报 Kafka
  → 追踪服务 按 traceId 聚合成链路树 → 存储 → 查看瀑布图
  → 日志与指标都带 traceId, 可「日志↔链路↔指标」跳转

采样策略:

策略说明适用
头部采样固定比例(如 10%)整链路采样简单,但热点链路可能被漏
尾采样按结果/耗时条件采样保留慢/错链路,成本更高
动态采样结合 QPS/错误率调整比例大规模生产

4.2 一次故障排查时序

用户投诉 → 看板发现错误率上升 → 告警触发 → 打开链路追踪定位慢/错服务
  → 关键字检索该服务日志(traceId) → 定位根因(如数据库慢查询)
  → 修复 → 告警恢复 → 复盘(事后分析, 补 SLO/告警规则)

一句话:可观测性三件套合起来就是「排查飞轮」——指标发现问题、追踪缩小范围、日志定位根因。

4.3 日志 ↔ 指标 ↔ 追踪的关联视图

数据分型存储,但排查要「一张图」:

  • 三个系统共用 时间戳 + service + trace_id 作为关联键。
  • 在链路瀑布图里点击一个 span → 直接跳到该时段的日志(按 trace_id 过滤)和该服务的指标曲线。
  • 看板以「服务拓扑」组织:每个节点显示健康指标,点击穿透到链路与日志。

关联视图的实现:统一维护「trace_id → 日志/指标的元数据」映射(写入端带 tag,查询端拼条件),本质是「元数据一致 + 查询跳转」,不强制三套数据放同一存储。

五、高可用与成本控制

5.1 高可用

  • 采集端:Agent 本地缓冲(磁盘),Kafka 不可用时不丢日志。
  • 管道:Kafka 多副本 + 多 broker,消费组重平衡自动恢复。
  • 存储:ClickHouse/ES 多副本 + 跨可用区;故障时降级为「只写不查」或「读本地热数据」。
  • 告警:规则引擎多实例 + 分布式锁,避免重复告警。

5.2 成本控制(核心面试点)

手段收益代价
采样日志/链路减少 50-90%覆盖不完全,需保证关键日志全量
分层存储热 7 天(SSD) → 温 30 天 → 冷(对象存储)冷数据查询慢
压缩 + 批量存储/带宽省 3-5 倍端到端延迟略增
字段裁剪只存必需字段排查时缺字段
低基数字段优化LowCardinality 省空间高基数需 rehash

优先级:关键业务日志不采样、INFO 以下采样、调试日志不入生产;告警依赖的指标全量保留。

5.3 采样配置示例

def should_sample(log_event):
    # 错误/关键路径全量,INFO 按比例
    if log_event.level in ("ERROR", "WARN"): return True
    if log_event.trace_id in BLACKLIST_SLOW_TRACES: return True
    return hash(log_event.service) % 10 == 0   # 10% INFO

5.4 告警自监控与容量

可观测系统自身也要被观测:

  • 管道健康:Kafka 消费 lag、写入积压、采集端缓冲水位,超过阈值自告警。
  • 存储健康:磁盘水位、写入拒绝率、查询慢日志。
  • 告警系统 HA:规则引擎多副本 + 分布式锁,避免单点挂了无人告警。
  • 容量规划:按大促峰值 × 1.5 冗余预留写入与存储,告警系统第一个不能被压垮。

一句话:监控系统的「最后一公里」是自监控——管道积压、存储写满、规则引擎失联都要有兜底告警。

六、查询与可视化

  • 查询语言:LogQL(Loki)/ PromQL(指标)/ SQL(ClickHouse)统一 DSL,支持按 service + 关键字 + 时间范围。
  • 看板:Grafana 统一面板,按服务/环境/集群分层。
  • SLO 看板:可用性、延迟(Apdex)、错误预算消耗,驱动告警优先级。
  • 相关性视图:同一 traceId 一键跳转日志、指标,减少排查跳转成本。

6.2 SLO 与错误预算驱动

告警不应该只看「某个指标超阈值」,而要以 SLO(服务目标)+ 错误预算 为纲:

概念说明
SLI服务质量指标:可用性(成功/总请求)、延迟(Apdex)、饱和度
SLO目标:如「月可用性 99.9%」「P95 延迟 < 200ms」
错误预算1 - SLO:如 99.9% 对应每月允许 43 分钟不可用
消耗速率错误预算消耗越快,越应触发告警与冻结高风险发布

用错误预算消耗率告警(而非绝对阈值)能大幅降噪:短暂抖动不告警,消耗加速才告警,把告警留给「用户可感知」的问题。

七、性能与扩展

  • 写入路径:从 Agent → Kafka → 存储全程批量 + 压缩,单节点日志写入可达百万级/秒(ClickHouse)。
  • 查询优化:时间分区裁剪、service 低基数字段先过滤、预聚合(rollup)加速看板。
  • 水平扩展:Kafka broker、ClickHouse 分片、查询网关全部可加机器扩展。
  • 容量规划:按「峰值 × 1.5 冗余」预留写入带宽,按保留期规划存储层。

八、权衡与备选

决策点本文选型备选权衡
日志存储ClickHouseElasticsearch / LokiCH 写入快、成本低;ES 全文强;Loki 最省
指标存储VictoriaMetricsPrometheus + ThanosVM 高基数友好;Prom 生态标准
链路存储ClickHouse 表Tempo / JaegerCH 统一运维;Tempo 与 Grafana 集成佳
采集OTel Agent + Filebeat全量自研OTel 标准开放;自研可深度定制
告警引擎自研 + 规则引擎Prometheus Alertmanager自研可做去重降噪;Alertmanager 开箱即用

取舍原则

  • 成本 > 极端完整:采样 + 分层是标配,追求 100% 全覆盖不可负担。
  • 告警质量 > 告警数量:宁可少而准,不要告警风暴。
  • 开放标准 > 锁定:优先 OTel、Prom 生态,避免被单一厂商锁死。

九、扩展场景与面试追问

9.1 全链路压测与容量规划

可观测性系统本身也要「可观测、可压测」:

  • 压测时注入海量日志,验证「写入管道不丢不堵、查询不拖垮」。
  • 提前规划 Kafka/存储容量:按业务高峰(大促)的 1.5 倍峰值预留,避免告警系统第一个被压垮。
  • 告警系统自身的告警:采集积压、Kafka 延迟、存储写满都要有自监控。

9.2 边缘场景:K8s 与 Serverless

  • K8s:Agent 以 DaemonSet 部署,Pod 漂移不影响采集;日志带 namespace/pod 标签。
  • Serverless:函数冷启动无常驻 Agent → 用平台侧日志转发 / OTel SDK 直发,注意冷启动期间的日志不丢。

9.3 日志合规与脱敏

生产日志可能包含用户隐私(手机号、身份证、卡号),合规设计:

  • 采集端脱敏:Agent 侧正则/字段规则打码(138****1234),源头不落原始 PII。
  • 权限隔离:日志按业务线/环境分权限,敏感日志只有指定角色可查(RBAC + 审计)。
  • 保留期治理:按数据类型设置不同 TTL,合规要求保留的冷存,其余到期清理。
  • 审计追踪:谁在什么时间查了谁的日志,本身要留痕。

9.4 面试常见追问

追问关键回答
日志太多查不快怎么办?时间分区裁剪 + 低基数字段前置过滤 + 采样 + 分阶段查询(先粗筛后精确)
采集端宕机会丢日志吗?Agent 本地缓冲 + 重试,缓冲满才丢弃(可配丢弃策略),关键业务双路冗余
告警风暴怎么治?告警分组 + 聚合去重 + 静默期 + 依赖树(根因告警优先)+ SLO 消耗率
trace 采样会不会漏掉故障?尾采样按错误/慢响应优先保留,保证故障链路不丢
日志与指标选型怎么定?日志看全文、指标看趋势:日志量大用 CH/Loki,指标用 TSDB;按查询场景各取所长

十、总结

模块关键点一句话记忆
采集Agent 批量 + 本地缓冲日志不能因传输抖动而丢
管道Kafka 削峰 + 压缩写与处理解耦
存储日志 CH / 指标 TSDB / 链路 CH各司其职、按数据类型选
追踪traceId 贯穿 + 采样故障排查的导航仪
告警规则 + 去重 + 路由数据变行动
成本采样 + 分层 + 低基数可观测性的经济学

一句话:日志监控题的面试主线是「采集 → 传输 → 存储 → 查询 → 告警」管道 + 「日志/指标/追踪」三件套 + 「采样/分层」成本控制,讲清写放大与查性能的权衡,就抓住了全部考点。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「design」更多文章

  1. 设计一个消息队列系统(类 Kafka)
  2. 设计搜索引擎
  3. 设计推荐系统