失败主因是上下文未透传至日志处理器:需用opentelemetry.sdk._logs.logginghandler替换原生handler,注册loggerprovider,日志格式添加%(oteltraceid)s和%(otelspanid)s。

为什么 Flask/FastAPI 自动注入 trace_id 失败?
OpenTelemetry 的自动插件(opentelemetry-instrumentation-flask 或 opentelemetry-instrumentation-fastapi)默认不会把 trace context 注入到 request 对象或日志中,你看到的 logging 里没有 trace_id,不是 SDK 没采样,而是上下文没透传到日志处理器。
必须显式配置 LoggingHandler 并启用 context propagation:
- 用
opentelemetry.sdk._logs.LoggingHandler替换原生StreamHandler - 确保
otel_tracer_provider已注册,且LoggerProvider启用了set_logger_provider - 在日志格式中加入
%(otelTraceID)s和%(otelSpanID)s占位符
如何让下游 HTTP 请求自动携带 traceparent?
OpenTelemetry 的 requests 插件(opentelemetry-instrumentation-requests)只负责「采集」,不自动修改请求头——除非你用的是 urllib3 或 httpx 的 instrumented 版本,且已调用 trace.get_current_span() 触发上下文激活。
常见失效场景:
- 手动 new 出的
requests.Session()没被 instrumented(需提前调用RequestsInstrumentor().instrument()) - 异步调用(如
asyncio.to_thread())导致 context 丢失,得用contextvars.ContextVar手动绑定 - 下游服务未启用 OpenTelemetry 接收器(如没配 OTLP exporter 或没开
/v1/tracesendpoint)
验证方式:打印 requests.Request.headers,确认含 traceparent 字段,格式为 00-{trace_id}-{span_id}-01。
FastAPI 中中间件和路由 span 名称重复怎么办?
默认情况下,opentelemetry-instrumentation-fastapi 会为每个请求创建两个 span:一个由中间件生成(名称为 HTTP GET),一个由路由 handler 生成(名称为 app:read_item)。这会导致 Jaeger/Tempo 里出现冗余节点,且 parent-child 关系错乱。
解决方法是禁用中间件自动 span,只保留路由级 span:
- 初始化时传参
excluded_urls="/health,/metrics"并设traced_request_attrs=["method", "url"] - 在
FastAPI实例化后,手动调用FastAPIInstrumentor.instrument_app(app, ...),并指定skip_depends_on=False - 更彻底的做法:卸载默认中间件,改用自定义
BaseHTTPMiddleware+tracer.start_as_current_span控制 span 生命周期
OTLP exporter 连不上 collector 怎么快速定位?
错误信息 ExportFailed: rpc error: code = Unavailable desc = connection refused 看似网络问题,实际常因协议/端口/TLSCert 不匹配。
检查顺序必须是:
- 确认 collector 配置监听的是
otlp-http还是otlp-grpc—— Python 默认走 gRPC,但很多本地 collector 示例用的是 http(端口4318) - 环境变量优先级高于代码参数:
OTEL_EXPORTER_OTLP_ENDPOINT必须明确写成http://localhost:4318或https://collector.example.com:4317 - 若用 HTTPS,
OTEL_EXPORTER_OTLP_CERTIFICATE必须指向 PEM 文件路径,不能是内容字符串 - 临时验证:用
curl -X POST http://localhost:4318/v1/traces -H "Content-Type: application/json" -d '{}'看 collector 是否响应 200
真正麻烦的是跨容器场景:Docker 中 Python 应用用 localhost 访问不到宿主机的 collector,得换 host.docker.internal 或用自定义 network。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











