open-telemetry / open-telemetry/opentelemetry-python-contrib

Quart apps are not autoinstrumented

Open
#4,994 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
1.1k
Forks
1.1k
Avg merge
4d 15h
Merged PRs (30d)
16

Description

Describe your environment

OS: macOS
Python version: Python 3.12.13
Package version: opentelemetry-distro 0.65b0, opentelemetry-instrumentation-flask 0.65b0
Other: quart 0.22.0, flask 3.1.3

What happened?

A Quart app running under opentelemetry-instrument produces no server spans. The equivalent flask app does.

Steps to Reproduce

Sample self-contained test:

❯ uv run --with quart==0.22.0 \
             --with opentelemetry-distro==0.65b0 \
             --with opentelemetry-instrumentation-flask==0.65b0 \
             --with opentelemetry-instrumentation-asgi==0.65b0 \
             --with pytest \
             pytest test_repro.py
# test_quart.py
import asyncio

from opentelemetry import trace
from opentelemetry.instrumentation.auto_instrumentation._load import _load_instrumentors
from opentelemetry.instrumentation.distro import DefaultDistro
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter


def test_quart_request_produces_a_server_span():
    exporter = InMemorySpanExporter()
    provider = TracerProvider()
    provider.add_span_processor(SimpleSpanProcessor(exporter))
    trace.set_tracer_provider(provider)

    # Equivalent to a `opentelemetry-instrument` run
    _load_instrumentors(DefaultDistro())

    # Only import quart after instrumenting
    from quart import Quart

    app = Quart(__name__)

    @app.get("/")
    async def index() -> str:
        return "ok"

    response = asyncio.run(app.test_client().get("/"))
    assert response.status_code == 200

    spans = exporter.get_finished_spans()
    server = [s for s in spans if s.kind is trace.SpanKind.SERVER]
    # This assert fails
    assert len(server) == 1
Expected Result

Expect to see server spans with the method, route, etc.

Actual Result

No server spans are emitted for Quart

Additional context

Most of the work can be done by the asgi instrumentation, with some additional logic for wiring up the http.route specifically.

Is there any preference to moving some logic down to asgi vs. having all the wiring done in a quart instrumentation?

Would you like to implement a fix?

Yes

Tip

React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Run the supplied Quart reproduction and start with _load_instrumentors(DefaultDistro()) and the existing ASGI instrumentation entry point. Compare the Quart and Flask loading paths, focusing on how the HTTP route is wired. Done means the reproduction emits one SERVER span for the Quart request with method and route information, while the existing instrumentation tests continue to pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend, observability
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
70/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.