可观测性规范
日志、指标、追踪与告警
详细的日志级别、结构化字段、脱敏规则见 日志规范。本章聚焦可观测性场景下的日志采集、检索与留存。
[Requirement] 所有服务的日志必须统一采集到集中式日志平台(ELK / Loki / SLS),禁止仅输出到本地文件。
[Standard] 日志采集架构:
应用容器 → stdout/stderr → Fluent Bit/Fluentd → 日志平台(ES/Loki)[Requirement] 日志必须为 JSON 结构化格式,包含以下必选字段:
| 字段 | 说明 | 示例 |
|---|---|---|
timestamp | ISO 8601,带毫秒 | 2024-01-01T12:00:00.123Z |
level | 日志级别 | INFO |
service | 服务名 | user-service |
trace_id | 链路追踪 ID | abc123def456 |
message | 日志消息 | 用户登录成功 |
[Standard] 生产环境日志级别默认 INFO,ERROR 及以上级别必须告警。
| 级别 | 采集 | 留存 | 告警 |
|---|---|---|---|
| DEBUG | 生产默认关闭,可动态开启 | 1 天 | 否 |
| INFO | 采集 | 7 天 | 否 |
| WARN | 采集 | 30 天 | 否 |
| ERROR | 采集 | 90 天 | 是 |
| FATAL | 采集 | 180 天 | 是(立即) |
[Standard] 日志平台必须支持按 trace_id、service、level、时间范围检索,单次查询响应时间 ≤ 5 秒。
- ❌ 日志只输出到本地文件,容器销毁后丢失。
- ❌ 日志使用
print()非结构化输出,无法检索。 - ❌ ERROR 日志不告警,故障无人感知。
[Requirement] 指标分为四类:黄金指标(RED)、资源指标、业务指标、自定义指标。
[Requirement] 每个 HTTP/RPC 接口必须暴露以下三个指标:
| 指标 | 说明 |
|---|---|
| Rate(请求量) | 每秒请求数 QPS |
| Errors(错误率) | 错误请求占比 |
| Duration(耗时) | 请求响应时间 P50/P95/P99 |
[Standard] 每个服务必须采集以下资源指标:
| 指标 | 说明 | 告警阈值参考 |
|---|---|---|
| CPU 使用率 | 容器/主机 CPU | > 80% 持续 5 分钟 |
| 内存使用率 | RSS 内存 | > 85% 持续 5 分钟 |
| 磁盘使用率 | 数据盘 | > 85% |
| 网络流入/流出 | 网络流量 | > 带宽 80% |
| 连接数 | 数据库/Redis 连接 | > 连接池 80% |
[Standard] 核心业务流程必须埋点上报业务指标:
| 业务 | 指标 |
|---|---|
| 电商 | 下单量、支付成功率、退款率 |
| 内容 | DAU、停留时长、点击率 |
| 通用 | 注册量、登录成功率、消息发送量 |
[Suggestion] 业务关键流程可自定义指标,如队列积压量、缓存命中率、重试次数等。
[Standard] 指标命名遵循 namespace_subsystem_name_unit 格式,使用 snake_case。
# 示例http_requests_total # 请求总数http_request_duration_seconds # 请求耗时(秒)db_connections_active # 活跃数据库连接数cache_hit_ratio # 缓存命中率business_order_created_total # 订单创建总数[Standard] 服务通过 /metrics 端点暴露 Prometheus 格式指标。
# FastAPI + prometheus_client 示例
from prometheus_client import Counter, Histogram, generate_latest
REQUEST_COUNT = Counter('http_requests_total', 'Total HTTP requests', ['method', 'endpoint', 'status'])
REQUEST_DURATION = Histogram('http_request_duration_seconds', 'HTTP request duration', ['endpoint'])
@app.middleware("http")
async def metrics_middleware(request, call_next):
start = time.time()
response = await call_next(request)
duration = time.time() - start
REQUEST_COUNT.labels(request.method, request.url.path, response.status_code).inc()
REQUEST_DURATION.labels(request.url.path).observe(duration)
return response
@app.get("/metrics")
async def metrics():
return Response(generate_latest(), media_type="text/plain")[Caution] 指标标签(label)的取值集合必须有限,禁止使用用户 ID、订单 ID 等高基数值作为 label,否则会导致指标爆炸。
# 正确:label 取值有限
REQUEST_COUNT.labels(method="GET", endpoint="/users", status="200").inc()
# 错误:user_id 是高基数 label
REQUEST_COUNT.labels(user_id="12345").inc()[Requirement] 全链路必须传播 trace_id,支持跨服务追踪。使用 W3C Trace Context 标准。
[Standard] 入口服务生成 trace_id,通过 HTTP Header traceparent 向下游传递。
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 │ └──────────── trace_id ────────────┘ └── span_id ──┘ │ └─ version └─ flags[Requirement] 以下操作必须创建 Span:
| 操作类型 | 说明 |
|---|---|
| 入站 HTTP 请求 | 创建根 Span |
| 出站 HTTP/RPC 调用 | 创建子 Span |
| 数据库操作 | 创建子 Span,包含 SQL 摘要 |
| 缓存操作 | 创建子 Span |
| 消息队列生产/消费 | 创建子 Span |
[Standard] 生产环境使用概率采样,默认采样率 10%,错误请求 100% 采样。
| 场景 | 采样率 |
|---|---|
| 正常请求 | 10% |
| 错误请求 | 100% |
| 慢请求(>1s) | 100% |
| 压测环境 | 100% |
[Standard] 使用 Jaeger / Zipkin / SkyWalking 作为追踪后端,UI 支持按 trace_id 查询完整调用链。
[Standard] Span 名称格式:<HTTP方法> <路由> 或 <服务名>.<方法名>。
# HTTP 入站GET /api/users/{id}
# RPC 调用order-service.createOrder
# 数据库SELECT FROM users WHERE id = ?[Requirement] 告警分为三级,不同级别通知方式和响应时间不同:
| 级别 | 名称 | 通知方式 | 响应时间 | 示例 |
|---|---|---|---|---|
| P0 | 紧急 | 电话 + 短信 + IM | 立即响应(5 分钟内) | 服务不可用、核心接口错误率 > 5% |
| P1 | 重要 | 短信 + IM | 15 分钟内响应 | 错误率 1-5%、响应时间劣化 |
| P2 | 一般 | IM | 1 小时内响应 | 磁盘使用率 > 85%、单实例异常 |
[Requirement] 每个服务必须配置以下告警规则:
| 告警项 | 条件 | 级别 |
|---|---|---|
| 服务不可用 | 健康检查失败 ≥ 1 分钟 | P0 |
| 错误率 | 5xx 错误率 > 1% 持续 3 分钟 | P0 |
| 响应时间 | P99 > 2s 持续 5 分钟 | P1 |
| CPU 使用率 | > 90% 持续 5 分钟 | P1 |
| 内存使用率 | > 90% 持续 5 分钟 | P1 |
| 磁盘使用率 | > 85% | P2 |
| 实例重启 | Pod 重启 ≥ 3 次/10 分钟 | P1 |
Prometheus AlertManager 规则:
groups:
- name: service-alerts
rules:
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m])) > 0.01
for: 3m
labels:
severity: P0
annotations:
summary: "服务 {{ $labels.service }} 错误率过高"
description: "错误率 {{ $value | humanizePercentage }},持续 3 分钟"
- alert: HighLatency
expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 2
for: 5m
labels:
severity: P1
annotations:
summary: "服务 {{ $labels.service }} 响应缓慢"
description: "P99 响应时间 {{ $value }}s"[Standard] 配置告警抑制规则,避免告警风暴:
- 父告警触发时,抑制子告警(如服务不可用时抑制该服务的所有子告警)
- 同类告警在 5 分钟内只通知一次
- 维护期间自动静默告警
- ❌ 告警阈值设得过低,导致告警疲劳,真正的告警被忽略。
- ❌ 所有告警都发到同一个群,无法区分紧急程度。
- ❌ 告警只发邮件,无人及时查看。
[Standard] Dashboard 分为三层,面向不同角色:
| 层级 | 受众 | 内容 |
|---|---|---|
| 全局总览 | 管理层、值班 | 所有服务健康状态、核心业务指标 |
| 服务视图 | 研发、运维 | 单个服务的 RED 指标、资源指标、依赖状态 |
| 实例视图 | 运维 | 单个实例的 CPU、内存、网络、日志 |
[Requirement] 必须包含以下面板:
- 所有服务健康状态(红/黄/绿)
- 全局 QPS、错误率、P99 响应时间
- 核心业务指标(订单量、支付成功率等)
- 正在触发的告警列表
[Standard] 每个服务必须有独立 Dashboard,包含:
| 面板组 | 指标 |
|---|---|
| 请求量 | QPS(按接口、按状态码) |
| 错误率 | 5xx 占比、4xx 占比 |
| 响应时间 | P50/P95/P99 延迟分布 |
| 资源使用 | CPU、内存、磁盘、网络 |
| 依赖状态 | 数据库连接池、Redis、下游服务 |
| JVM/运行时 | 线程数、GC 次数、堆内存(Java) |
[Standard] Dashboard 设计遵循以下原则:
- 一屏可见:核心指标在一屏内展示,无需滚动
- 时间对齐:同一 Dashboard 所有面板使用相同时间范围
- 阈值标线:关键指标面板标注告警阈值线
- 颜色编码:正常绿色、警告黄色、异常红色
- 可下钻:点击异常指标可跳转到日志或追踪详情
- ❌ Dashboard 放满几十个面板,关键指标被淹没。
- ❌ 不同面板时间范围不一致,无法关联分析。
- ❌ 只有技术指标,没有业务指标,无法判断业务影响。