open-telemetry / open-telemetry/opentelemetry-python-contrib
traced_execution_async` in `opentelemetry-instrumentation-dbapi` ignores instrumentation suppression
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
opentelemetry-instrumentation-dbapi0.62b1opentelemetry-instrumentation-psycopg0.62b1- psycopg 3 with
AsyncConnection/AsyncConnectionPool - Python 3.14
What happened?
Spans are still created (and exported) for queries executed inside
opentelemetry.instrumentation.utils.suppress_instrumentation() when the
cursor is async. The sync path correctly suppresses.
Steps to Reproduce
from opentelemetry.instrumentation.psycopg import PsycopgInstrumentor
from opentelemetry.instrumentation.utils import suppress_instrumentation
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
from psycopg import AsyncConnection
exporter = InMemorySpanExporter()
provider = TracerProvider()
provider.add_span_processor(SimpleSpanProcessor(exporter))
PsycopgInstrumentor().instrument(tracer_provider=provider)
async def main():
async with await AsyncConnection.connect(DSN) as conn:
async with conn.cursor() as cur:
with suppress_instrumentation():
await cur.execute("SELECT 1")
# after running main():
assert exporter.get_finished_spans() == () # FAILS — one span was exported
The same code with a sync psycopg.connect() passes.
Expected Result
No span — the async path should honor suppression exactly as the sync path
does.
Actual Result
One span was created and exported for the SELECT 1 executed inside suppress_instrumentation() — exporter.get_finished_spans() returns a span named SELECT with db.statement = "SELECT 1", and the final assertion in the reproducer fails.
Running the identical code with a sync psycopg.connect() connection produces no span — the sync path honors suppression, the async path does not.
Additional context
The common use of suppress_instrumentation() around background polls
(outbox sweeps, health/metric COUNT queries) silently stops working the day a
codebase migrates from sync to async connections — every poll becomes a root
span again, with no error and no signal that suppression is inert. Tests that
assert is_instrumentation_enabled() inside the block keep passing, because
the flag is set correctly; it is simply never read.
Would you like to implement a fix?
None
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
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 in the opentelemetry-instrumentation-dbapi async cursor execution path and compare it with the sync path, using the provided psycopg AsyncConnection reproducer. Verify the change by checking that the suppression block produces no finished spans, while the existing sync behavior remains unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- observability
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100