跳转至

Ch 49 日志、监控、审计与告警

面包屑

本书主页Part VIII 治理与复盘 › Ch 49

项目第 3 年 · 成熟与治理期——监控审计


本章你将学到

  • CloudWatch/CloudTrail 与审计日志持久化(含仪表板布局与结构化日志 schema)
  • SNS 告警与运营 runbook(含告警疲劳治理)
  • 数据平台可观测性 vs 应用可观测性的区别

引申:双联章说明

本章与 Ch 50 排障与可观测性实战 合构"监控排障双联章"——本章讲"怎么看见"(监控/告警/审计),Ch 50 讲"怎么排查"(决策树/walkthrough/预防)。建议两章连读。


49.1 CloudWatch/CloudTrail 与审计日志持久化

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#edf5ff','primaryTextColor':'#161616','primaryBorderColor':'#0f62fe','lineColor':'#697077','secondaryColor':'#d9fbfb','tertiaryColor':'#f2f4f8','fontSize':'14px'}}}%%
flowchart TB
 subgraph 可观测体系["平台可观测体系"]
 subgraph AWS原生["AWS 原生监控"]
 CW@{ icon: "logos:aws-cloudwatch", form: "rounded", label: CloudWatch<br/>日志/指标/告警, pos: "b", h: 40 }
 CT@{ icon: "logos:aws-cloudtrail", form: "rounded", label: CloudTrail<br/>API 调用审计, pos: "b", h: 40 }
 end

 subgraph 平台元数据["平台元数据"]
 AUDIT@{ icon: "logos:aws-redshift", form: "rounded", label: 审计日志表<br/>Redshift metadata schema, pos: "b", h: 40 }
 DDB_STATUS@{ icon: "logos:aws-dynamodb", form: "rounded", label: DynamoDB<br/>任务状态表, pos: "b", h: 40 }
 end

 subgraph AI可观测["Agentic BI 可观测"]
 LANGFUSE@{ icon: "codicon:hubot", form: "rounded", label: Langfuse<br/>Agent 链路追踪, pos: "b", h: 36 }
 PROM[Prometheus<br/>AI 指标]
 end
 end
classDef bpProcess fill:#edf5ff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpData fill:#d9fbfb,stroke:#007d79,stroke-width:2px,color:#161616
classDef bpDecision fill:#fcf4d6,stroke:#f1c21b,stroke-width:2px,color:#161616
classDef bpSuccess fill:#defbe6,stroke:#198038,stroke-width:2px,color:#161616
classDef bpError fill:#fff1f1,stroke:#da1e28,stroke-width:2px,color:#161616
classDef bpExternal fill:#f2f4f8,stroke:#697077,stroke-width:2px,color:#161616
classDef bpGroup fill:#ffffff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpInfo fill:#f6f2ff,stroke:#8a3ffc,stroke-width:2px,color:#161616
class CW,CT,PROM bpProcess
class AUDIT,DDB_STATUS bpData
class LANGFUSE bpInfo
linkStyle default stroke:#697077,stroke-width:2px

图 49-1 CloudWatch/CloudTrail 与审计日志持久化

CloudWatch

功能 用途
Logs Glue/Lambda/Step Functions 日志
Metrics 资源指标(CPU/内存/吞吐)+ 自定义指标
Alarms 阈值告警(如 Glue 失败率 > 5%)
Dashboards 可视化面板

表 49-1 CloudWatch

CloudWatch 仪表板布局设计

数据平台的仪表板不能照搬应用监控那套。应用监控盯着"请求延迟/错误率",数据平台要盯的是"数据对不对、及时不及时、完整不完整"。平台仪表板切成四个区,每个区盯一类健康度:

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#edf5ff','primaryTextColor':'#161616','primaryBorderColor':'#0f62fe','lineColor':'#697077','secondaryColor':'#d9fbfb','tertiaryColor':'#f2f4f8','fontSize':'14px'}}}%%
flowchart TB
 subgraph 仪表板["CloudWatch 仪表板四分区"]
 Q1@{ icon: "codicon:error", form: "rounded", label: ETL 健康区<br/>任务成功率/失败率/运行时长/队列深度, pos: "b", h: 36 }
 Q2[数据新鲜度区<br/>各域最新批次时间/SLA 达成率/行数对账]
 Q3@{ icon: "logos:aws-s3", form: "rounded", label: 成本区<br/>各服务月度花费/RPU 利用率/S3 存储增长, pos: "b", h: 40 }
 Q4@{ icon: "codicon:shield", form: "rounded", label: AI 指标区<br/>查询准确率/延迟/缓存命中/护栏拦截, pos: "b", h: 36 }
 end
classDef bpProcess fill:#edf5ff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpData fill:#d9fbfb,stroke:#007d79,stroke-width:2px,color:#161616
classDef bpDecision fill:#fcf4d6,stroke:#f1c21b,stroke-width:2px,color:#161616
classDef bpSuccess fill:#defbe6,stroke:#198038,stroke-width:2px,color:#161616
classDef bpError fill:#fff1f1,stroke:#da1e28,stroke-width:2px,color:#161616
classDef bpExternal fill:#f2f4f8,stroke:#697077,stroke-width:2px,color:#161616
classDef bpGroup fill:#ffffff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpInfo fill:#f6f2ff,stroke:#8a3ffc,stroke-width:2px,color:#161616
class Q1,Q2 bpProcess
class Q3,Q4 bpInfo
linkStyle default stroke:#697077,stroke-width:2px

图 49-2 CloudWatch 仪表板布局设计

仪表板分区 核心指标 告警阈值
ETL 健康 任务成功率、失败率、运行时长 P95、就绪队列深度 失败率 >5% / P95 超基线 2×
数据新鲜度 各域最新批次时间、SLA 达成率、行数对账偏差 新鲜度 >24h / 对账偏差 >0.1%
成本 各服务月度花费、RPU 利用率、S3 存储增长 月花费超预算 10%
AI 指标 查询准确率、P50/P95 延迟、缓存命中率、护栏拦截率 准确率 <85% / P95 >30s

表 49-2 CloudWatch 仪表板布局设计

结构化日志 schema 设计

数据平台的日志必须结构化。非结构化的纯文本日志排障时基本没法检索——你试过在大段文本里 grep 关键字段就知道多痛苦。平台所有组件(Glue/Lambda/Step Functions)统一用 structlog 输出 JSON 日志,字段约定如下:

# 示意:structlog 结构化日志 schema(所有组件统一字段)
import structlog
logger = structlog.get_logger()

# 核心意图:统一字段让 CloudWatch Logs Insights 可跨组件检索
logger.info("etl_completed",
    batch_id="20260618-001500",          # 批次标识,贯穿全链路([Ch 20](./20-元数据管理与数据血缘.md))
    component="glue",                    # 组件:glue/lambda/sf/redshift
    domain="ma",                         # 业务域
    entity="doctor_master",              # 实体
    duration_sec=142,                    # 耗时
    row_count=12450,                     # 行数
    status="success",                    # success/failed/retry
    severity="INFO")                     # INFO/WARN/ERROR
字段 类型 说明
batch_id string 批次标识,贯穿 Landing→Raw→Enriched→Redshift 全链路
component string 组件类型(glue/lambda/sf/redshift)
domain string 业务域(ma/sci/retail/...)
entity string 数据实体(doctor_master/fact_prescription/...)
duration_sec int 耗时(秒)
row_count int 处理行数
status string success/failed/retry
severity string INFO/WARN/ERROR

表 49-3 核心意图:统一字段让 CloudWatch Logs Insights 可跨组件检索

排障时用 CloudWatch Logs Insights 按字段检索——比如查"ma 域最近失败的批次":filter domain="ma" and status="failed" | sort @timestamp desc。没有结构化字段,这种查询根本没戏。

CloudTrail

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#edf5ff','primaryTextColor':'#161616','primaryBorderColor':'#0f62fe','lineColor':'#697077','secondaryColor':'#d9fbfb','tertiaryColor':'#f2f4f8','fontSize':'14px'}}}%%
flowchart LR
 subgraph CloudTrail["CloudTrail 审计"]
 T1[记录所有 AWS API 调用<br/>谁/何时/做了什么]
 T2@{ icon: "logos:aws-s3", form: "rounded", label: 持久化到 S3<br/>长期归档, pos: "b", h: 40 }
 T3@{ icon: "logos:aws-cloudtrail", form: "rounded", label: 与审计日志互补<br/>CloudTrail 管基础设施层<br/>审计日志管数据层, pos: "b", h: 40 }
 end
classDef bpProcess fill:#edf5ff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpData fill:#d9fbfb,stroke:#007d79,stroke-width:2px,color:#161616
classDef bpDecision fill:#fcf4d6,stroke:#f1c21b,stroke-width:2px,color:#161616
classDef bpSuccess fill:#defbe6,stroke:#198038,stroke-width:2px,color:#161616
classDef bpError fill:#fff1f1,stroke:#da1e28,stroke-width:2px,color:#161616
classDef bpExternal fill:#f2f4f8,stroke:#697077,stroke-width:2px,color:#161616
classDef bpGroup fill:#ffffff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpInfo fill:#f6f2ff,stroke:#8a3ffc,stroke-width:2px,color:#161616
class T1 bpProcess
class T2 bpData
class T3 bpInfo
linkStyle default stroke:#697077,stroke-width:2px

图 49-3 CloudTrail

引申

CloudTrail 和平台审计日志互补——CloudTrail 记录"谁调用了什么 AWS API"(如谁执行了 terraform apply),审计日志记录"哪个 ETL 任务加载了什么数据"。前者是基础设施层审计,后者是数据层审计。两者结合提供全栈可审计性。


49.2 SNS 告警与运营 runbook

告警体系

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#edf5ff','primaryTextColor':'#161616','primaryBorderColor':'#0f62fe','lineColor':'#697077','secondaryColor':'#d9fbfb','tertiaryColor':'#f2f4f8','fontSize':'14px'}}}%%
flowchart LR
 subgraph 告警链路["告警链路"]
 SOURCE@{ icon: "logos:aws-cloudwatch", form: "rounded", label: "告警源<br/>CloudWatch Alarm / 自定义", pos: "b", h: 40 } --> SNS[SNS Topic]
 SNS --> EMAIL[邮件通知]
 SNS --> SLACK[即时通讯通知]
 SNS --> LAMBDA[Lambda 自动处理]
 end
classDef bpProcess fill:#edf5ff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpData fill:#d9fbfb,stroke:#007d79,stroke-width:2px,color:#161616
classDef bpDecision fill:#fcf4d6,stroke:#f1c21b,stroke-width:2px,color:#161616
classDef bpSuccess fill:#defbe6,stroke:#198038,stroke-width:2px,color:#161616
classDef bpError fill:#fff1f1,stroke:#da1e28,stroke-width:2px,color:#161616
classDef bpExternal fill:#f2f4f8,stroke:#697077,stroke-width:2px,color:#161616
classDef bpGroup fill:#ffffff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpInfo fill:#f6f2ff,stroke:#8a3ffc,stroke-width:2px,color:#161616
class SOURCE bpDecision
class SNS,LAMBDA bpProcess
class EMAIL,SLACK bpExternal
linkStyle default stroke:#697077,stroke-width:2px

图 49-4 告警体系

告警级别 触发条件 通知方式 响应时间
P0 紧急 生产数据丢失/平台不可用 电话+短信+邮件 15 分钟
P1 高 关键 ETL 失败/数据质量告警 即时通讯+邮件 1 小时
P2 中 非关键任务失败/性能告警 邮件 4 小时
P3 低 资源使用率告警 日报汇总 次日

表 49-4 告警体系

运营 Runbook

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#edf5ff','primaryTextColor':'#161616','primaryBorderColor':'#0f62fe','lineColor':'#697077','secondaryColor':'#d9fbfb','tertiaryColor':'#f2f4f8','fontSize':'14px'}}}%%
flowchart TD
 ALERT[收到告警] --> CLASSIFY{告警分类}
 CLASSIFY -->|P0/P1|RUNBOOK[查阅 Runbook<br/>标准处置流程]
 RUNBOOK --> STEP1[① 确认影响范围]
 STEP1 --> STEP2[② 定位根因]
 STEP2 --> STEP3[③ 执行修复]
 STEP3 --> STEP4[④ 验证恢复]
 STEP4 --> STEP5[⑤ 事后复盘<br/>更新 Runbook]
classDef bpProcess fill:#edf5ff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpData fill:#d9fbfb,stroke:#007d79,stroke-width:2px,color:#161616
classDef bpDecision fill:#fcf4d6,stroke:#f1c21b,stroke-width:2px,color:#161616
classDef bpSuccess fill:#defbe6,stroke:#198038,stroke-width:2px,color:#161616
classDef bpError fill:#fff1f1,stroke:#da1e28,stroke-width:2px,color:#161616
classDef bpExternal fill:#f2f4f8,stroke:#697077,stroke-width:2px,color:#161616
classDef bpGroup fill:#ffffff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpInfo fill:#f6f2ff,stroke:#8a3ffc,stroke-width:2px,color:#161616
class ALERT,CLASSIFY bpDecision
class RUNBOOK bpInfo
class STEP1,STEP2,STEP3,STEP4 bpProcess
class STEP5 bpSuccess
linkStyle default stroke:#697077,stroke-width:2px

图 49-5 运营 Runbook

Runbook 要素 说明
影响范围 告警影响哪些业务/数据
根因排查 按决策树定位问题
修复步骤 标准化的修复操作
验证方法 如何确认已恢复
事后复盘 记录原因+改进措施

表 49-5 运营 Runbook

Trade-off

告警太少会漏掉问题,太多会"告警疲劳"(忽略重要告警)。好的告警体系遵循"可行动性"原则——每个告警都应该有明确的处置动作。如果一个告警"看到了也不知道怎么办",它不应该存在——要么改为日报,要么补充 Runbook。

告警疲劳治理

告警疲劳是运维圈的隐形杀手。你想想,每天收 200 条告警,谁还分得出哪条是真正要命的?P0 很快就淹在噪音里。平台用了三招对付告警疲劳:

治理手段 做法 效果
分级 P0/P1/P2/P3 四级,P0 电话+短信,P3 仅日报汇总 高级别不被低级别淹没
去重 同一告警源 5 分钟内只通知一次 避免抖动产生告警风暴
聚合窗口 同类告警在 15 分钟窗口内聚合成一条 "3 个 Glue Job 失败"而非 3 条独立告警

表 49-6 告警疲劳治理

# 示意:告警去重与聚合
def should_notify(alert: dict, window=300) -> bool:
    # 核心意图:去重 + 聚合,避免告警风暴
    key = (alert["source"], alert["entity"])
    if key in recent_alerts and time.time() - recent_alerts[key] < window:
        return False                          # 5 分钟内同类告警不重复通知
    recent_alerts[key] = time.time()
    return True

数据平台特有的可观测场景

数据平台有一类可观测场景,做应用的人几乎遇不到:ETL 成功但数据为空。任务返回值是 success,但落地行数 0——可能是源系统当天没数据,也可能是过滤条件写错了,还可能是上游断流了。传统监控只看"任务失败了没",这种"看似成功实则异常"的场景根本不会被抓到:

场景 传统监控 数据平台可观测
ETL 任务失败 ✅ 告警 ✅ 告警
ETL 任务成功但行数=0 ❌ 不告警 ✅ 告警(行数低于历史基线)
ETL 任务成功但行数骤降 50% ❌ 不告警 ✅ 告警(行数偏差超阈值)
数据新鲜度 >24h ❌ 不告警 ✅ 告警(SLA 违约)

表 49-7 数据平台特有的可观测场景

这就是 Ch 51 数据可观测性里提到的"数据新鲜度/质量"维度。行数对账(Ch 17)的告警不能只看"过没过",还得看"行数是不是异常低"。平台的做法:每个实体的行数和 7 天滚动基线比,偏差超过 50% 就告警。


49.3 引申:数据平台可观测性 vs 应用可观测性

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#edf5ff','primaryTextColor':'#161616','primaryBorderColor':'#0f62fe','lineColor':'#697077','secondaryColor':'#d9fbfb','tertiaryColor':'#f2f4f8','fontSize':'14px'}}}%%
flowchart TB
 subgraph 区别["数据平台 vs 应用可观测性"]
 APP@{ icon: "codicon:error", form: "rounded", label: 应用可观测性<br/>关注:请求延迟/错误率/吞吐<br/>Metrics/Logging/Tracing, pos: "b", h: 36 }
 DATA@{ icon: "codicon:graph", form: "rounded", label: 数据平台可观测性<br/>关注:数据新鲜度/质量/血缘/SLA<br/>+ Metrics/Logging/Tracing, pos: "b", h: 36 }
 end
classDef bpProcess fill:#edf5ff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpData fill:#d9fbfb,stroke:#007d79,stroke-width:2px,color:#161616
classDef bpDecision fill:#fcf4d6,stroke:#f1c21b,stroke-width:2px,color:#161616
classDef bpSuccess fill:#defbe6,stroke:#198038,stroke-width:2px,color:#161616
classDef bpError fill:#fff1f1,stroke:#da1e28,stroke-width:2px,color:#161616
classDef bpExternal fill:#f2f4f8,stroke:#697077,stroke-width:2px,color:#161616
classDef bpGroup fill:#ffffff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpInfo fill:#f6f2ff,stroke:#8a3ffc,stroke-width:2px,color:#161616
class APP bpExternal
class DATA bpInfo
linkStyle default stroke:#697077,stroke-width:2px

图 49-6 引申:数据平台可观测性 vs 应用可观测性

维度 应用可观测性 数据平台可观测性
核心指标 延迟/错误率/吞吐 数据新鲜度/质量通过率/SLA 达成率
关注点 请求是否成功 数据是否正确/及时/完整
血缘 通常无 必需(数据从哪来到哪去)
质量 不涉及 核心维度(行数对账/约束校验)
工具 APM(如 Datadog) 数据可观测平台(如 Monte Carlo)

表 49-8 引申:数据平台可观测性 vs 应用可观测性

引申

数据平台可观测性是近年新概念——传统 APM 工具关注"系统是否正常",但数据平台还需要"数据是否正常"。一个 ETL 可能"成功执行"但"数据为空"——传统监控不会告警,但数据可观测性工具会(行数异常下降)。如果今天重建,建议引入数据可观测平台(如 Monte Carlo/Bigeye)作为传统监控的补充。


本章小结

  • 可观测体系:CloudWatch(日志/指标/告警)+ CloudTrail(API 审计)+ 平台审计日志(数据层)+ Langfuse/ Prometheus(AI 层)
  • CloudWatch 仪表板四分区:ETL 健康 / 数据新鲜度 / 成本 / AI 指标——数据平台关注"数据是否正确/及时/完整"而非仅"请求延迟"
  • 结构化日志 schema(structlog JSON,统一 batch_id/component/domain/entity 等字段)让 CloudWatch Logs Insights 可跨组件检索
  • CloudTrail 管基础设施层审计,平台审计日志管数据层审计——互补提供全栈可审计性
  • SNS 告警四级(P0-P3)+ 运营 Runbook(影响→根因→修复→验证→复盘)——可行动性原则;告警疲劳治理(分级+去重+聚合窗口)
  • 数据平台特有可观测:ETL 成功但数据为空/行数骤降——行数与 7 天滚动基线对比告警
  • 数据平台可观测性 ≠ 应用可观测性:还需关注数据新鲜度/质量/血缘/SLA——数据可观测平台是演进方向

下一章

Ch 50 排障与可观测性实战 —— 监控体系讲完了,接下来看实战中怎么排障。

评论