Skip to content

Fix asyncio to_thread instrumentation to measure actual function execution - #4901

Open
bourbonkk wants to merge 2 commits into
open-telemetry:mainfrom
bourbonkk:fix/asyncio-to-thread-duration
Open

Fix asyncio to_thread instrumentation to measure actual function execution#4901
bourbonkk wants to merge 2 commits into
open-telemetry:mainfrom
bourbonkk:fix/asyncio-to-thread-duration

Conversation

@bourbonkk

Copy link
Copy Markdown
Contributor

Description

trace_to_thread returned the target function unwrapped and recorded the span and metrics at wrap time: the asyncio to_thread-<name> span ended before the function started running in the worker thread, asyncio.process.duration recorded ~0, and exceptions raised by the function were never observed (state was always "finished").

This change makes trace_to_thread return a functools.wraps wrapper that starts the timer/span and records metrics around the actual call, making the existing exception handling reachable.

It also drops the _is_instrumented guard in this path: the wrapper is created per to_thread() call so there is no re-wrapping risk, and marking the user's function meant every call after the first was not instrumented at all.

Fixes #4900

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

  • New unit tests: span/histogram duration covers a time.sleep(0.1) execution, exception path (span ERROR status + exception event, metric state="exception"), repeated calls each instrumented.
  • Full instrumentation-asyncio test suite passes on Python 3.10, 3.12, and 3.14.

Does This PR Require a Core Repo Change?

  • No.

Checklist:

  • Followed the style guidelines of this project
  • Changelogs have been updated
  • Unit tests have been added
  • Documentation has been updated

🤖 Generated with Claude Code

trace_to_thread returned the function unwrapped and recorded the span
and metrics at wrap time, so durations were ~0 and exceptions raised in
the worker thread were never observed. Return a functools.wraps wrapper
that records around the real call instead. Drop the _is_instrumented
guard: wrappers are created per call, and marking the user's function
made every call after the first invisible.

Fixes open-telemetry#4900
@bourbonkk
bourbonkk requested a review from a team as a code owner July 30, 2026 14:05
@tammy-baylis-swi tammy-baylis-swi moved this to Ready for review in Python PR digest Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Ready for review

2 participants