关于 monitoring-setup
monitoring-setup 是面向 Python/FastAPI 服务的可观测性配置技能,指导设置指标、结构化日志、追踪、告警、健康检查和仪表板。它还要求将配置摘要写入 monitoring-config.md。
主要功能
- 01Prometheus 指标配置
- 02structlog 结构化日志
- 03OpenTelemetry 分布式追踪
- 04告警规则与阈值设计
- 05健康检查端点规划
- 06Grafana 仪表板结构
适用场景
- 01为 FastAPI 服务规划可观测性
- 02为 API 端点设计 RED 指标
- 03配置生产服务的告警规则
- 04整理并记录监控配置
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ hieutrtr/ ai1-skills --skill monitoring-setup安装后在 Claude Code 中输入/monitoring-setup确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/alert-rules-template.ymldashboard-template.jsonlogging-config-template.pymetrics-config-template.py详细介绍
监控设置
适用场景
遇到以下情况时,启用此技能:
- 为 Python/FastAPI 应用设置结构化日志记录
- 配置 Prometheus 指标采集以及自定义计数器/直方图
- 实现健康检查端点(存活和就绪)
- 为生产服务设计告警规则和阈值
- 创建用于服务监控的 Grafana 仪表板
- 集成 Sentry 进行错误跟踪和性能监控
- 使用 OpenTelemetry 实现分布式追踪
- 审查或改进现有的可观测性覆盖范围
输出: 将可观测性配置摘要写入 monitoring-config.md,记录已设置的内容(指标、告警、仪表板、健康检查)。
请勿在以下情况下使用此技能:
- 响应正在发生的生产事故(使用
incident-response) - 部署监控基础设施(使用
deployment-pipeline) - 编写应用业务逻辑(使用
python-backend-expert) - 配置 Docker 容器(使用
docker-best-practices)
说明
可观测性的四大支柱
每个生产服务都必须实现全部四大支柱。
┌─────────────────────────────────────────────────────────────────┐
│ OBSERVABILITY │
├────────────────┬───────────────┬──────────────┬────────────────┤
│ METRICS │ LOGGING │ TRACING │ ALERTING │
│ │ │ │ │
│ Prometheus │ structlog │ OpenTelemetry│ Alert rules │
│ counters, │ structured │ distributed │ thresholds, │
│ histograms, │ JSON logs, │ trace spans, │ notification │
│ gauges │ context │ correlation │ channels │
├────────────────┴───────────────┴──────────────┴────────────────┤
│ DASHBOARDS (Grafana) │
│ Visualize metrics, logs, and traces in one place │
└─────────────────────────────────────────────────────────────────┘
支柱 1:指标(Prometheus)
对于请求驱动型服务,使用 RED 方法;对于资源,使用 USE 方法。
RED 方法(适用于每个 API 端点):
- Rate —— 每秒请求数
- Errors —— 每秒失败请求数
- Duration —— 请求延迟分布
USE 方法(适用于基础设施资源):
- Utilization —— 资源使用百分比(CPU、内存、磁盘)
- Saturation —— 排队或等待的工作(连接池、队列深度)
- Errors —— 错误事件(OOM 终止、连接失败)
需要采集的关键指标:
from prometheus_client import Counter, Histogram, Gauge, Info
# RED metrics
REQUEST_COUNT = Counter(
"http_requests_total",
"Total HTTP requests",
labelnames=["method", "endpoint", "status_code"],
)
REQUEST_DURATION = Histogram(
"http_request_duration_seconds",
"HTTP request duration in seconds",
labelnames=["method", "endpoint"],
buckets=[0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0],
)
# USE metrics
DB_POOL_USAGE = Gauge(
"db_connection_pool_usage",
"Database connection pool utilization",
labelnames=["pool_name"],
)
DB_POOL_SIZE = Gauge(
"db_connection_pool_size",
"Database connection pool max size",
labelnames=["pool_name"],
)
REDIS_CONNECTIONS = Gauge(
"redis_active_connections",
"Active Redis connections",
)
# Business metrics
ACTIVE_USERS = Gauge(
"active_users_total",
"Currently active users",
)
APP_INFO = Info(
"app",
"Application metadata",
)
用于自动采集指标的 FastAPI 中间件:
import time
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
class PrometheusMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
method = request.method
endpoint = request.url.path
start_time = time.perf_counter()
response = await call_next(request)
duration = time.perf_counter() - start_time
status_code = str(response.status_code)
REQUEST_COUNT.labels(
method=method, endpoint=endpoint, status_code=status_code
).inc()
REQUEST_DURATION.labels(
method=method, endpoint=endpoint
).observe(duration)
return response
完整设置请参阅 references/metrics-config-template.py。
支柱 2:日志记录(structlog)
使用带有上下文信息的结构化 JSON 日志。生产环境中绝不使用 print() 或非结构化日志记录。
日志记录原则:
- 结构化 —— JSON 格式,可由机器解析
- 包含上下文 —— 每条日志都包含请求 ID、用户 ID、追踪 ID
- 分级 —— 使用恰当的日志级别(DEBUG、INFO、WARNING、ERROR、CRITICAL)
- 可操作 —— 每条 WARNING/ERROR 日志都应指出需要调查的内容
日志级别及其适用场景:
| 级别 | 适用场景 | 示例 |
|---|---|---|
| DEBUG | 详细的诊断信息,生产环境中禁用 | Processing item 42 of 100 |
| INFO | 正常操作、重要事件 | User created, Payment processed |
| WARNING | 意外但已处理的情况 | Retry attempt 2 of 3, Cache miss |
| ERROR | 操作失败,需要关注 | Database query failed, External API timeout |
| CRITICAL | 系统级故障,需要立即处理 | Cannot connect to database, Out of memory |
structlog 设置:
import structlog
structlog.configure(
processors=[
structlog.contextvars.merge_contextvars,
structlog.stdlib.filter_by_level,
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.UnicodeDecoder(),
structlog.processors.JSONRenderer(),
],
wrapper_class=structlog.stdlib.BoundLogger,
context_class=dict,
logger_factory=structlog.stdlib.LoggerFactory(),
cache_logger_on_first_use=True,
)
添加请求上下文:
from starlette.middleware.base import BaseHTTPMiddleware
import structlog
import uuid
class LoggingContextMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
request_id = request.headers.get("X-Request-ID", str(uuid.uuid4()))
structlog.contextvars.clear_contextvars()
structlog.contextvars.bind_contextvars(
request_id=request_id,
method=request.method,
path=request.url.path,
)
response = await call_next(request)
response.headers["X-Request-ID"] = request_id
return response
完整设置请参阅 references/logging-config-template.py。
支柱 3:追踪(OpenTelemetry)
分布式追踪可跨服务边界关联日志和指标。
FastAPI 追踪设置:
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
from opentelemetry.instrumentation.redis import RedisInstrumentor
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
def setup_tracing(app, service_name: str = "backend"):
resource = Resource.create({"service.name": service_name})
provider = TracerProvider(resource=resource)
exporter = OTLPSpanExporter(endpoint="http://otel-collector:4317")
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)
# Auto-instrument FastAPI, SQLAlchemy, Redis
FastAPIInstrumentor.instrument_app(app)
SQLAlchemyInstrumentor().instrument()
RedisInstrumentor().instrument()
为业务逻辑添加自定义 Span:
tracer = trace.get_tracer(__name__)
async def process_order(order_id: str):
with tracer.start_as_current_span("process_order") as span:
span.set_attribute("order.id", order_id)
with tracer.start_as_current_span("validate_order"):
await validate_order(order_id)
with tracer.start_as_current_span("charge_payment"):
result = await charge_payment(order_id)
span.set_attribute("payment.status", result.status)
with tracer.start_as_current_span("send_confirmation"):
await send_confirmation(order_id)
支柱 4:告警
告警必须可执行。每条告警都应指出出了什么问题以及该怎么做。
告警设计原则:
- 仅针对影响用户的问题触发页面通知——不要针对非紧急警告触发页面通知
- 根据 SLO 设置阈值——不要使用随意设定的数字
- 避免告警疲劳——如果某条告警频繁触发却无需采取行动,就修复或移除它
- 包含操作手册链接——每条告警都应链接到修复指南
- 使用多窗口消耗速率——更快地检测问题,同时避免误报
典型 FastAPI 应用的告警阈值:
| 告警 | 条件 | 严重程度 | 操作 |
|---|---|---|---|
| 错误率高 | http_requests_total{status=~"5.."} 在 5 分钟内 > 总请求数的 5% | SEV2 | 检查日志,考虑回滚 |
| 延迟高 | http_request_duration_seconds p99 在 5 分钟内 > 2s | SEV3 | 检查 DB 查询和依赖项 |
| 服务宕机 | 健康检查连续 2 分钟失败 | SEV1 | 重启、检查日志并升级处理 |
| DB 连接数偏高 | 连接池使用率在 5 分钟内 > 80% | SEV3 | 检查是否存在连接泄漏 |
| DB 连接数严重偏高 | 连接池使用率在 2 分钟内 > 95% | SEV2 | 重启应用并调查原因 |
| 内存使用率高 | 容器内存使用率在 10 分钟内 > 85% | SEV3 | 检查是否存在内存泄漏 |
| 磁盘空间不足 | 磁盘使用率 > 85% | SEV3 | 清理日志并扩展卷 |
| 证书即将过期 | SSL 证书将在 < 14 天后过期 | SEV4 | 续订证书 |
请参阅 references/alert-rules-template.yml 中的 Prometheus 告警规则。
健康检查端点
每个服务都必须公开两个健康检查端点。
**存活状态(/health):**进程是否正在运行?如果应用处于存活状态,则返回 200。
**就绪状态(/health/ready):**服务是否能够处理请求?检查所有依赖项。
from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import text
from datetime import datetime, timezone
router = APIRouter(tags=["health"])
@router.get("/health")
async def liveness():
"""Liveness probe -- is the process running?"""
return {
"status": "healthy",
"timestamp": datetime.now(timezone.utc).isoformat(),
"version": settings.APP_VERSION,
}
@router.get("/health/ready")
async def readiness(db: AsyncSession = Depends(get_db)):
"""Readiness probe -- can we handle traffic?"""
checks = {}
# Check database
try:
await db.execute(text("SELECT 1"))
checks["database"] = {"status": "ok", "latency_ms": 0}
except Exception as e:
checks["database"] = {"status": "error", "error": str(e)}
# Check Redis
try:
start = time.perf_counter()
await redis.ping()
latency = (time.perf_counter() - start) * 1000
checks["redis"] = {"status": "ok", "latency_ms": round(latency, 2)}
except Exception as e:
checks["redis"] = {"status": "error", "error": str(e)}
all_ok = all(c["status"] == "ok" for c in checks.values())
return JSONResponse(
status_code=200 if all_ok else 503,
content={
"status": "ready" if all_ok else "not_ready",
"checks": checks,
"timestamp": datetime.now(timezone.utc).isoformat(),
},
)
使用 Sentry 进行错误追踪
Sentry 会捕获未处理的异常和性能数据。
设置:
import sentry_sdk
from sentry_sdk.integrations.fastapi import FastApiIntegration
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
sentry_sdk.init(
dsn=settings.SENTRY_DSN,
environment=settings.APP_ENV,
release=settings.APP_VERSION,
traces_sample_rate=0.1, # 10% of requests for performance monitoring
profiles_sample_rate=0.1,
integrations=[
FastApiIntegration(),
SqlalchemyIntegration(),
],
# Do not send PII
send_default_pii=False,
# Filter out health check noise
before_send=filter_health_checks,
)
def filter_health_checks(event, hint):
"""Do not send health check errors to Sentry."""
if "request" in event and event["request"].get("url", "").endswith("/health"):
return None
return event
仪表板设计
Grafana 仪表板应采用一致的布局模式。
标准仪表板分区:
- 概览行——一目了然地查看关键 SLI(错误率、延迟、吞吐量)
- RED 指标行——每个端点的速率、错误和持续时间
- 基础设施行——CPU、内存、磁盘、网络
- 依赖项行——数据库、Redis、外部 API 的运行状况
- 业务指标行——应用特有的 KPI
仪表板布局:
┌─────────────────────────────────────────────────────────┐
│ Service Overview │
│ [Error Rate %] [p99 Latency] [Requests/s] [Uptime] │
├────────────────────────┬────────────────────────────────┤
│ Request Rate │ Error Rate │
│ (by endpoint) │ (by endpoint, status code) │
├────────────────────────┼────────────────────────────────┤
│ Latency (p50/p95/p99) │ Active Connections │
│ (by endpoint) │ (DB pool, Redis) │
├────────────────────────┴────────────────────────────────┤
│ Infrastructure │
│ [CPU %] [Memory %] [Disk %] [Network IO] │
├─────────────────────────────────────────────────────────┤
│ Dependencies │
│ [DB Latency] [Redis Latency] [External API Status] │
└─────────────────────────────────────────────────────────┘
请参阅 references/dashboard-template.json,获取完整的 Grafana 仪表板模板。
正常运行时间监控
外部正常运行时间监控从用户的角度验证服务。
外部监控内容:
- 从多个地理区域检查
/health端点 - 关键用户界面页面(登录、仪表板、API 文档)
- SSL 证书有效性和到期时间
- DNS 解析时间
建议的检查间隔:
| 检查项 | 间隔 | 超时 | 区域 |
|---|---|---|---|
| 健康检查端点 | 30 秒 | 10 秒 | 3 个或更多区域 |
| 关键页面 | 1 分钟 | 15 秒 | 2 个或更多区域 |
| SSL 证书 | 6 小时 | 30 秒 | 1 个区域 |
| DNS 解析 | 5 分钟 | 5 秒 | 3 个或更多区域 |
快速参考
完整模板请参阅 references/:logging-config-template.py、metrics-config-template.py、alert-rules-template.yml、dashboard-template.json。
新服务监控清单
- 已配置 structlog,并启用 JSON 输出
- 已配置请求日志中间件,并关联请求 ID
- 已在
/metrics公开 Prometheus 指标端点 - 已埋点 RED 指标(请求数、错误数、持续时间)
- 已实现健康检查端点(
/health、/health/ready) - 已初始化 Sentry SDK,并设置环境和发布版本标签
- 已定义错误率、延迟和可用性的告警规则
- 已创建包含标准分区的 Grafana 仪表板
- 已配置外部正常运行时间监控
- 已定义日志保留策略(默认:30 天)
输出文件
将监控配置摘要写入 monitoring-config.md:
# Monitoring Configuration: [Service Name]
## Metrics
| Metric | Type | Labels | Purpose |
|--------|------|--------|---------|
| http_requests_total | Counter | method, endpoint, status | RED: Request rate |
| http_request_duration_seconds | Histogram | method, endpoint | RED: Latency |
## Alerts
| Alert | Condition | Severity | Runbook |
|-------|-----------|----------|---------|
| HighErrorRate | error_rate > 5% for 5m | SEV2 | docs/runbooks/high-error-rate.md |
## Health Checks
- `/health` — Liveness probe
- `/health/ready` — Readiness probe (checks DB, Redis)
## Dashboards
- Grafana: Service Overview (imported from references/dashboard-template.json)
## Next Steps
- Run `/deployment-pipeline` to deploy with monitoring enabled
- Run `/incident-response` if alerts fire