FastAPI服务OpenTelemetry自动埋点失效,无x-b3响应头求助
FastAPI配置OpenTelemetry自动埋点后响应头缺失x-b3-spanid和x-b3-traceid字段
我正在尝试为FastAPI服务配置OpenTelemetry自动埋点,但始终无法成功,核心问题是服务响应头中缺少x-b3-spanid和x-b3-traceid字段。即使请求时携带已有的Brave链路追踪信息,响应里还是看不到这两个目标头。
我的当前配置
main.py代码
import pkg_resources from fastapi import FastAPI from fastapi import APIRouter from typing import Dict,Any from opentelemetry import trace from contextlib import asynccontextmanager from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware import logging LOG = logging.getLogger(__name__) ''' root_logger = logging.getLogger() root_logger.setLevel(logging.DEBUG) for handler in root_logger.handlers: handler.setLevel(logging.DEBUG) logconfig_dict.update({"root": {"level":"DEBUG", "handlers": ["error"]}}) ''' ''' from opentelemetry import trace, propagators, baggage from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import ( BatchSpanProcessor, ConsoleSpanExporter, ) provider = TracerProvider() processor = BatchSpanProcessor(ConsoleSpanExporter()) provider.add_span_processor(processor) trace.set_tracer_provider(provider) ''' tags_metadata = [ { "name": "root", "description": "root route", } ] app = FastAPI( title="fastapi-otel-test", description="This is a REST api that test otel with fastapi.", version="0.1.2", openapi_tags=tags_metadata, ) # Freeze OpenAPI verstion. OpenAPI 3.1.0 is not fully backward compatible app.openapi_version = "3.0.3" app.add_middleware(ProxyHeadersMiddleware) ''' def server_request_hook(span, scope: Dict[str, Any]): if span and span.is_recording(): span.set_attribute("service.name", "fastapi-otel") FastAPIInstrumentor.instrument_app(app, tracer_provider=provider, server_request_hook=server_request_hook) ''' FastAPIInstrumentor.instrument_app(app) @asynccontextmanager async def lifespan(app: FastAPI): # initialize LOG.error("lifespan() start") yield LOG.info("lifespan() end") router = APIRouter() import httpx @router.get("/", tags=["root"]) async def root(): async with httpx.AsyncClient() as client: response = await client.get("http://localhost:8000/foo") LOG.info(response.headers) return "Hello world" @router.get("/foo", tags=["root"]) async def foo(): return "Hello foo" app.include_router(router)
启动命令
./.venv/bin/fastapi dev main.py
测试请求及结果
$ curl -s http://localhost:8000/ -i -H "x-b3-span-id: deadbeef" -H "x-b3-trace-id: baadf00ddeadbeef" HTTP/1.1 200 OK date: Thu, 10 Oct 2024 22:20:13 GMT server: uvicorn content-length: 13 content-type: application/json "Hello world"
requirements.txt
fastapi[standard]==0.115.0 httpx opentelemetry-sdk opentelemetry-instrumentation-fastapi
Python版本:3.12
遗漏的配置及解决方案
问题出在以下几个关键配置缺失:
未配置B3 Propagator
OpenTelemetry默认使用W3C Trace Context格式的propagator,而B3格式(x-b3-*头)需要显式启用。需要安装opentelemetry-propagator-b3包,并配置全局propagator。未初始化TracerProvider
当前代码中TracerProvider的配置被注释,没有正确初始化追踪核心组件,导致链路追踪无法正常生成和传播。未配置Span导出及处理器
没有添加Span处理器和导出器,追踪数据无法被处理和输出,同时也会影响头信息的生成。Instrumentation时机问题
FastAPIInstrumentor.instrument_app应该在TracerProvider和Propagator配置完成后执行,确保instrumentation使用正确的全局配置。
修改后的完整配置
首先更新requirements.txt,添加B3 propagator依赖:
fastapi[standard]==0.115.0 httpx opentelemetry-sdk opentelemetry-instrumentation-fastapi opentelemetry-propagator-b3
然后修改main.py:
import pkg_resources from fastapi import FastAPI from fastapi import APIRouter from typing import Dict,Any from opentelemetry import trace from opentelemetry.propagate import set_global_textmap from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import ( BatchSpanProcessor, ConsoleSpanExporter, ) from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor from opentelemetry.propagators.b3 import B3MultiFormat from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware import logging LOG = logging.getLogger(__name__) # 初始化TracerProvider并配置Span处理器 provider = TracerProvider() processor = BatchSpanProcessor(ConsoleSpanExporter()) provider.add_span_processor(processor) trace.set_tracer_provider(provider) # 配置B3格式的Propagator set_global_textmap(B3MultiFormat()) tags_metadata = [ { "name": "root", "description": "root route", } ] app = FastAPI( title="fastapi-otel-test", description="This is a REST api that test otel with fastapi.", version="0.1.2", openapi_tags=tags_metadata, ) # Freeze OpenAPI verstion. OpenAPI 3.1.0 is not fully backward compatible app.openapi_version = "3.0.3" app.add_middleware(ProxyHeadersMiddleware) # 自定义请求钩子(可选,用于添加服务名等属性) def server_request_hook(span, scope: Dict[str, Any]): if span and span.is_recording(): span.set_attribute("service.name", "fastapi-otel") # 配置FastAPI Instrumentation FastAPIInstrumentor.instrument_app( app, tracer_provider=provider, server_request_hook=server_request_hook ) @asynccontextmanager async def lifespan(app: FastAPI): # initialize LOG.error("lifespan() start") yield LOG.info("lifespan() end") router = APIRouter() import httpx @router.get("/", tags=["root"]) async def root(): async with httpx.AsyncClient() as client: response = await client.get("http://localhost:8000/foo") LOG.info(response.headers) return "Hello world" @router.get("/foo", tags=["root"]) async def foo(): return "Hello foo" app.include_router(router)
验证修改
- 重新安装依赖:
pip install -r requirements.txt - 启动服务:
./.venv/bin/fastapi dev main.py - 再次发送测试请求:
curl -s http://localhost:8000/ -i -H "x-b3-span-id: deadbeef" -H "x-b3-trace-id: baadf00ddeadbeef"
此时响应头中应该会包含x-b3-spanid和x-b3-traceid字段,同时控制台会输出Span的详细追踪信息。
内容的提问来源于stack exchange,提问作者feroze
相关产品推荐
相关产品推荐

