下载工作台
FastAPI 开发

Prometheus、OpenTelemetry 与 APM

试读上半部分 · 解锁后可读全文

第 18 章 · Prometheus、OpenTelemetry 与 APM

本章目标:使用 prometheus-fastapi-instrumentatorsvc-demo 暴露 /metrics(请求计数、延迟直方图);理解 RED 指标Grafana 告警概念;入门 OpenTelemetry FastAPI instrumentation,让 trace_id 贯穿 ORM、Redis 与后台任务;将结构化日志metrics 通过 request_id 关联(衔接 ch08);了解 APM(Datadog / New Relic / SkyWalking 类工具)概念;建立生产可观测性 checklist

学时建议:5~6 小时(含 2 小时 Prometheus + Grafana 跟练)

前置:本模块 ch08 请求 ID 与结构化日志;ch10 Celery 异步;ch11 Redis 限流;ch17 压测基线。监控概念可对照 ops-deployxiaozi-cloud 告警章节。


18.1 可观测性三大支柱

         Metrics              Logs               Traces
    Prometheus/Grafana    JSON → ELK/Loki    OpenTelemetry → Jaeger
    「系统是否异常?」     「发生了什么?」      「慢在哪里?」
支柱svc-demo 示例
Metrics/metrics 暴露 http_requests_totalhttp_request_duration_seconds
Logsrequest_id=abc123 关联访问与审计日志
Tracestrace_id 串联 ORM 查询、Redis 缓存与 Celery 任务
统一使用虚构 svc-demoapi.example.com严禁写入真实监控地址、API Key 或租户 ID。

RED 方法(请求型 API 服务):

字母含义Prometheus 示例
RRate 每秒请求rate(http_requests_total[5m])
EErrors 错误率rate(http_requests_total{status=~"5.."}[5m])
DDuration 延迟分布histogram_quantile(0.95, ...)

18.2 prometheus-fastapi-instrumentator

安装:

pip install prometheus-fastapi-instrumentator

svc_demo/main.py

from fastapi import FastAPI
from prometheus_fastapi_instrumentator import Instrumentator

app = FastAPI(title="svc-demo", version="1.0.0")

@app.on_event("startup")
async def startup():
    Instrumentator(
        should_group_status_codes=True,
        should_ignore_untemplated=True,
        excluded_handlers=["/metrics", "/health", "/docs", "/openapi.json"],
    ).instrument(app).expose(app, endpoint="/metrics", include_in_schema=False)

默认采集:

指标说明
http_requests_total按 method、handler、status 计数
http_request_duration_seconds延迟直方图
http_requests_in_progress进行中请求(若启用)

自定义业务指标

from prometheus_client import Counter, Histogram

ORDER_CREATED = Counter(
    "svc_demo_orders_created_total",
    "成功创建订单数",
    ["tenant"],
)
DB_QUERY_LATENCY = Histogram(
    "svc_demo_db_query_seconds",
    "数据库查询耗时",
    ["operation"],
)

# 业务代码中
ORDER_CREATED.labels(tenant="demo").inc()
with DB_QUERY_LATENCY.labels(operation="list_products").time():
    await db.execute(stmt)

/metrics 安全

实践原因
不对外网暴露可能泄露内部路由结构
Nginx IP 白名单仅 Prometheus 可抓
include_in_schema=False不出现在 /docs
独立端口(选修):9090/metrics sidecar

docker-compose.prod.yml 中 Prometheus 抓取:

scrape_configs:
  - job_name: svc-demo
    metrics_path: /metrics
    static_configs:
      - targets: ["api:8000"]

18.3 Grafana 与告警(概念)

Grafana 连接 Prometheus 数据源,常用面板:

面板PromQL 思路
QPSsum(rate(http_requests_total{job="svc-demo"}[5m]))
错误率5xx / total
p95 延迟histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
饱和度进程 CPU、连接池(自定义 exporter)

告警规则示例(概念 YAML):

groups:
  - name: svc-demo
    rules:
      - alert: HighErrorRate
        expr: |
          sum(rate(http_requests_total{status=~"5.."}[5m]))
          / sum(rate(http_requests_total[5m])) > 0.05
        for: 5m
        labels: {severity: warning}
        annotations:
          summary: "svc-demo 5xx 错误率 > 5%"
      - alert: HighLatencyP95
        expr: |
          histogram_quantile(0.95,
            sum(rate(http_request_duration_seconds_bucket[5m])) by (le)
          ) > 1.0
        for: 10m
        labels: {severity: warning}

告警通知:邮件、钉钉、PagerDuty 等;staging 先验证,避免告警风暴。


18.4 OpenTelemetry 链路追踪

OpenTelemetry(OTel)提供 vendor-neutral 的 Traces / Metrics / Logs API。

安装:

pip install opentelemetry-api opentelemetry-sdk \
  opentelemetry-instrumentation-fastapi \
  opentelemetry-instrumentation-sqlalchemy \
  opentelemetry-instrumentation-httpx \
  opentelemetry-exporter-otlp

svc_demo/core/telemetry.py

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor

def setup_telemetry(app, engine):
    provider = TracerProvider()
    provider.add_span_processor(
        BatchSpanProcessor(
            OTLPSpanExporter(endpoint="otel-collector.example.com:4317", insecure=True)
        )
    )
    trace.set_tracer_provider(provider)
    FastAPIInstrumentor.instrument_app(app)
    SQLAlchemyInstrumentor().instrument(engine=engine.sync_engine)

main.py 启动时调用 setup_telemetry(app, engine)

trace_id 与 request_id 关联

ch08 中间件已注入 request_id;在 span 中写入:

from opentelemetry import trace

tracer = trace.get_tracer(__name__)

@app.middleware("http")
async def bind_trace_context(request: Request, call_next):
    span = trace.get_current_span()
    request_id = request.state.request_id  # ch08 中间件设置
    span.set_attribute("request.id", request_id)
    response = await call_next(request)
    response.headers["X-Trace-Id"] = format(span.get_span_context().trace_id, "032x")
    return response

结构化日志(structlog / logging JSON):

logger.info(
    "product_created",
    request_id=request_id,
    trace_id=format(trace_id, "032x"),
    product_id=product.id,
)

在 Jaeger UI 搜索 trace_id,可看到:HTTP → SQLAlchemy → Redis 全链路。


以下内容需解锁后阅读

试读已结束。解锁本章 ¥5.00,或开通年度会员畅读全部教程。
年度会员 ¥199.00/年; 小紫 AI 工作台有效会员 ¥99.00/年

正文仅在服务端鉴权后下发,未付费无法获取下半部分内容。