open-telemetry / open-telemetry/opentelemetry-python-contrib
FastAPI: Context change in sync route does not appear in background tasks
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 1.1k
- Forks
- 1.1k
- Avg merge
- 4d 15h
- Merged PRs (30d)
- 16
Description
Describe your environment
OS: Ubuntu
Python version: 3.11.0
Package version:
opentelemetry-api==1.34.1
opentelemetry-sdk==1.34.1
opentelemetry-instrumentation-fastapi==0.55b1
What happened?
When having a sync route in FastAPI and launching a background task, the changes done to the context do not appear in the context of the background task.
Steps to Reproduce
from fastapi import BackgroundTasks, FastAPI
from opentelemetry import context, trace
from opentelemetry.context import set_value
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
app = FastAPI()
# =========================================================================== #
# Optional: Bug is reproducible with or without setuping the resource, tracer
# provider and FastAPI instrumentor,
resource = Resource.create(attributes={SERVICE_NAME: "pricing-recommendation-engine"})
provider = TracerProvider(resource=resource)
trace.set_tracer_provider(provider)
# provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
FastAPIInstrumentor().instrument_app(app)
# =========================================================================== #
def my_background_task():
print(context.get_current())
def add_test_value_to_context():
ctx = context.get_current()
ctx = set_value("test_key", "test_value", context=ctx)
context.attach(ctx)
@app.get("/sync-endpoint")
def sync_endpoint(background_tasks: BackgroundTasks):
print(context.get_current())
add_test_value_to_context()
print(context.get_current())
background_tasks.add_task(my_background_task)
return "Hello, World!"
@app.get("/async-endpoint")
async def async_endpoint(background_tasks: BackgroundTasks):
print(context.get_current())
add_test_value_to_context()
print(context.get_current())
background_tasks.add_task(my_background_task)
return "Hello, World!"
uvicorn fastapi_context_bug:app
On endpoint /sync-endpoint, we get this:
{'current-span-b13884d6-004f-41b3-84b0-8704e8205ff6': _Span(name="GET /sync-endpoint", context=SpanContext(trace_id=0x3d1bdba09cbb92564db952045e92ab94, span_id=0x93ec8565cd311ed4, trace_flags=0x01, trace_state=[], is_remote=False))}
{'current-span-b13884d6-004f-41b3-84b0-8704e8205ff6': _Span(name="GET /sync-endpoint", context=SpanContext(trace_id=0x3d1bdba09cbb92564db952045e92ab94, span_id=0x93ec8565cd311ed4, trace_flags=0x01, trace_state=[], is_remote=False)), 'test_key': 'test_value'}
INFO: 127.0.0.1:45994 - "GET /sync-endpoint HTTP/1.1" 200 OK
{'current-span-b13884d6-004f-41b3-84b0-8704e8205ff6': _Span(name="GET /sync-endpoint", context=SpanContext(trace_id=0x3d1bdba09cbb92564db952045e92ab94, span_id=0x93ec8565cd311ed4, trace_flags=0x01, trace_state=[], is_remote=False))}
On endpoint /async-endpoint, we get this:
{'current-span-b13884d6-004f-41b3-84b0-8704e8205ff6': _Span(name="GET /async-endpoint", context=SpanContext(trace_id=0xf1009be126c6edf441c7f465fcc05e7e, span_id=0xfa1e818754376b97, trace_flags=0x01, trace_state=[], is_remote=False))}
{'current-span-b13884d6-004f-41b3-84b0-8704e8205ff6': _Span(name="GET /async-endpoint", context=SpanContext(trace_id=0xf1009be126c6edf441c7f465fcc05e7e, span_id=0xfa1e818754376b97, trace_flags=0x01, trace_state=[], is_remote=False)), 'test_key': 'test_value'}
INFO: 127.0.0.1:48510 - "GET /async-endpoint HTTP/1.1" 200 OK
{'current-span-b13884d6-004f-41b3-84b0-8704e8205ff6': _Span(name="GET /async-endpoint", context=SpanContext(trace_id=0xf1009be126c6edf441c7f465fcc05e7e, span_id=0xfa1e818754376b97, trace_flags=0x01, trace_state=[], is_remote=False)), 'test_key': 'test_value'}
Expected Result
The sync route should behave as the async route.
Actual Result
See above.
Additional context
No response
Would you like to implement a fix?
None
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at FastAPIInstrumentor and trace how sync and async routes run background tasks and propagate context. Reproduce the difference between /sync-endpoint and /async-endpoint, then add coverage showing that the sync route preserves the changed context in its background task and run the relevant instrumentation tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- fastapi, python
- Domain
- api, backend, observability
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100