用 Python + OpenTelemetry 给 Flask 接口做一次可落地的耗时归因:从埋点到 Grafana 看板

举报
yd_46858814 发表于 2026/11/04 01:24:08 2026/11/04
【摘要】 用 OpenTelemetry 给 Flask 接口做耗时归因:自动埋点看到请求全貌,手动 Span 拆解关键业务段,trace_id 串联日志,最后用 Grafana 三张图定位慢在 SQL、Redis 还是下游调用。

问题从哪里来

很多 Flask 接口变慢时,第一反应是看 CPU 或数据库慢查询,但真正难的是回答一个具体问题:一次请求里的 800 毫秒,到底花在了路由逻辑、SQL、Redis,还是下游 HTTP 调用上?只看平均耗时会把不同分支混在一起,p95 升高也找不到责任方。本文用 OpenTelemetry 给一个 Flask 服务做最小可落地的耗时归因:埋点一次,之后每条慢请求都能按 Span 拆开。

方案总览

  • 追踪:OpenTelemetry Python SDK + Flask、SQLAlchemy、requests 的自动埋点,再对关键业务段手动加 Span。
  • 导出:开发环境先用 OTLP 导出到本地 Jaeger 或 Tempo;生产按需采样,避免全量追踪拖垮服务。
  • 呈现:Grafana 里按接口、按 Span 名称聚合,看 p50/p95 和耗时占比,而不是只看总量。
  • 闭环:把 trace_id 写进业务日志,线上复现时先拿 trace_id 再定位,不再靠猜时间窗口翻日志。

第一步:接入自动埋点

先安装 SDK 与埋点包:

pip install opentelemetry-sdk opentelemetry-exporter-otlp opentelemetry-instrumentation-flask opentelemetry-instrumentation-sqlalchemy opentelemetry-instrumentation-requests

在应用启动时初始化,注意要先于创建 Flask 应用或在应用工厂里尽早执行:

from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor

resource = Resource.create({"service.name": "order-api", "service.version": "1.0.0"})
provider = TracerProvider(resource=resource)
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")))
trace.set_tracer_provider(provider)

FlaskInstrumentor().instrument_app(app)
SQLAlchemyInstrumentor().instrument(engine=engine)
RequestsInstrumentor().instrument()

只做这一步,你已经能看到每个请求的根 Span,以及它下面的 SQL 与 HTTP 子 Span。很多团队停在这里就够用了,但业务逻辑本身仍是黑盒。

第二步:给关键业务段手动加 Span

自动埋点看不到“查库存”“拼装价格”“写审计日志”这些业务步骤的耗时。手动加 Span 时只包关键段,不要给每个小函数都加,否则看板会被噪声淹没:

from opentelemetry import trace

tracer = trace.get_tracer(__name__)

def build_order(user_id, items):
    with tracer.start_as_current_span("check_inventory") as span:
        span.set_attribute("user.id", user_id)
        span.set_attribute("items.count", len(items))
        check_inventory(items)
    with tracer.start_as_current_span("calc_price"):
        calc_price(items)
    with tracer.start_as_current_span("save_order"):
        save_order(user_id, items)

给 Span 加业务属性时要克制:只加能用于分组和过滤的字段,例如用户类型、渠道、商品类目;不要把手机号、订单备注、完整请求体塞进属性,既占存储又带来合规风险。

第三步:让日志和追踪互通

只看追踪会丢业务上下文,只看日志又找不到耗时链路。做法是在日志格式里固定输出 trace_id 和 span_id,复现时先从日志拿到 trace_id,再去 Jaeger 或 Tempo 里打开整条链路:

import logging
from opentelemetry import trace

class TraceIdFilter(logging.Filter):
    def filter(self, record):
        span = trace.get_current_span()
        ctx = span.get_span_context() if span else None
        record.trace_id = format(ctx.trace_id, "032x") if ctx and ctx.is_valid else "-"
        return True

把这个 Filter 挂到业务日志器上,日志行形如 trace_id=... level=INFO msg=订单创建完成,排查时直接按 trace_id 检索。

第四步:在 Grafana 里读懂耗时

接好数据源后,建议先做三张图,而不是一上来就做复杂看板:

  1. 按接口分组的 p50/p95 耗时趋势,确认慢的是个别接口还是整体抬升。
  2. 按 Span 名称聚合的耗时占比,确认慢请求里最大头是 SQL、Redis 还是下游调用。
  3. 慢请求样本表,列出 trace_id、接口、总耗时和最耗时子 Span,点进去能直接看到完整链路。

读图时注意区分两种慢:一种是某个子 Span 本身变慢,例如 SQL 从 20 毫秒涨到 400 毫秒;另一种是子 Span 数量变多,例如一次请求里循环调用了 30 次下游接口,总耗时被调用次数放大。前者优化单次执行,后者要先消除 N+1 式调用。

采样与成本控制

全量追踪在流量大时成本很高。一个可落地的起点是:

  • 开发与联调:全量采样,方便排障。
  • 生产:按 1% 到 10% 采样,并对错误请求和慢请求做尾部采样,确保异常样本一定保留。
  • 采样决策要尽早做,并把采样率、保留周期写进运行文档,避免半年后没人知道数据为什么突然变少。

常见坑

  • 埋点包版本与 Flask、SQLAlchemy 版本不匹配,导致 Span 缺失或应用启动失败:升级前先在测试环境验证组合。
  • 在异步任务或线程池里丢了上下文:跨线程传递 context,或在任务入口重新建立 Span,避免子任务变成孤儿 Span。
  • 把追踪当成性能测试工具:追踪擅长归因单次慢请求,不擅长压测结论;压测仍需用专门工具看吞吐与错误率。
  • 只接追踪不接告警:至少给 p95 耗时和错误率设阈值告警,否则看板只是事后复盘工具。

小结

耗时归因不需要一开始就上全套可观测平台。先用自动埋点看到请求全貌,再对两三个关键业务段手动加 Span,把 trace_id 写进日志,最后用三张 Grafana 图把“慢在哪里”变成可复现的结论。按这个顺序落地,一周内就能让下一次接口变慢的排查从翻日志猜测,变成打开一条链路直接定位。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。