Python 分布式追踪的端到端实践

4次阅读

opentelemetry python trace 不自动跨进程传递,因 http 请求头默认不携带 trace 上下文,需显式启用传播器并手动注入/提取;属性写入失败多因命名、类型或覆盖问题;di 和异步场景需注意 span 生命周期;exporter 配置须匹配协议与路径。

Python 分布式追踪的端到端实践

为什么 opentelemetry-python 的 trace 不会自动跨进程传递

因为 HTTP 请求头默认不携带 trace 上下文,服务间调用时 Tracer 无法从入参中提取 parent span。你看到的每个服务都是独立 root span,根本连不成一条线。

  • 必须显式启用传播器(如 TraceContextTextMapPropagator),否则 inject()extract() 不生效
  • HTTP 客户端(如 requests)需手动注入 headers:调用 propagator.inject(carrier=request.headers)
  • fastapi/flask 等框架里,得在中间件里用 propagator.extract(carrier=request.headers) 恢复 context
  • 异步服务(httpx.AsyncClient)同样要注入,但 carrier 要用 dictMultiDict,不能直接传 request.headers 对象

span.set_Attribute() 写进去了却在 ui 里看不到

属性写入时机不对,或 key 名触发了后端过滤规则。OpenTelemetry Collector 默认会丢弃以 http.net. 开头但不符合语义约定的属性。

  • 优先用标准语义约定字段,比如 span.set_attribute("http.status_code", 404),而不是自造 "status"
  • 自定义属性名避免点号,"user_id" 可以,"user.id" 在某些后端(如 Jaeger)会被截断或忽略
  • 值类型必须是基本类型(str/int/bool/Float),传 datetimedict 会静默失败
  • 如果用了 OTEL_Resource_ATTRIBUTES 环境变量设 service.name,但代码里又调 resource.Resource.create(),后者会覆盖前者——属性就丢了

fastapi-injectordependency-injector 时 span 生命周期错乱

依赖注入容器创建的对象生命周期和 span 不对齐,常见表现是 span 提前结束、attribute 写到错误 span、甚至 panic 报 Span is already ended

  • 不要在 DI provider 里直接调 tracer.start_span();改用 with tracer.start_as_current_span() 包裹业务逻辑
  • 若需跨多个注入对象传递 span,用 context.get_current() + context.attach() 显式传递 context,别依赖“当前 span”自动延续
  • 异步依赖(AsyncProvider)必须确保 await 发生在 span 活跃期内,否则 await 后再 set_attribute 就晚了
  • 测试时用 trace.TracerProvider(NoOpTracerProvider()) 替换真实 provider,否则 mock 不干净会导致 context 泄漏

本地开发时 OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 总连不上

不是地址写错,而是 endpoint 协议和端口不匹配:4318 是 HTTP/gRPC 的默认分界点,但 OTLP/HTTP 必须带 /v1/traces 路径,且 collector 需开启 otlphttp receiver。

立即学习Python免费学习笔记(深入)”;

  • 确认 collector 配置含 receivers: [otlp, otlphttp],且 otlphttp: 下有 endpoint: 0.0.0.0:4318
  • Python 端 endpoint 应为 http://localhost:4318/v1/traces(HTTP)或 http://localhost:4317(gRPC)
  • docker 环境下别用 localhost,改用宿主机网关(host.docker.internal)或 compose network 别名
  • 启动 collector 后执行 curl -v http://localhost:4318/v1/status,返回 200 才算通——光 ping 通端口没用

跨服务 trace 最容易卡在 propagation 和 exporter 配置这两步,其他地方出问题,大概率是 context 没传进去,或者传进去了但被中间件/装饰器意外清掉了。

text=ZqhQzanResources