Skip to content

Commit b19d9e6

Browse files
fix: deduplicate urllib3 spans when PoolManager delegates to HTTPConnectionPool (#82)
1 parent 7cfd148 commit b19d9e6

1 file changed

Lines changed: 37 additions & 37 deletions

File tree

drift/instrumentation/urllib3/instrumentation.py

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import base64
44
import json
55
import logging
6+
from contextvars import ContextVar
7+
from functools import partial
68
from typing import Any
79
from urllib.parse import parse_qs, urlencode, urlparse
810

@@ -55,6 +57,10 @@ def __init__(self, message: str, method: str, url: str):
5557
# When these are active, urllib3 should skip creating duplicate spans
5658
HIGHER_LEVEL_HTTP_INSTRUMENTATIONS = {"RequestsInstrumentation"}
5759

60+
# Set to True inside the PoolManager patch so that the HTTPConnectionPool patch
61+
# can detect the call originated from PoolManager and skip duplicate span creation.
62+
_inside_poolmanager = ContextVar("_inside_poolmanager", default=False)
63+
5864
HEADER_SCHEMA_MERGES = {
5965
"headers": SchemaMerge(match_importance=0.0),
6066
}
@@ -167,6 +173,9 @@ def patched_urlopen(
167173
# Set calling_library_context to suppress socket instrumentation warnings
168174
# for internal socket calls made by urllib3
169175
context_token = calling_library_context.set("urllib3")
176+
# Signal to the HTTPConnectionPool patch that this call originated
177+
# from PoolManager so it should skip creating a duplicate span.
178+
pm_token = _inside_poolmanager.set(True)
170179
try:
171180

172181
def original_call():
@@ -189,6 +198,7 @@ def original_call():
189198
span_kind=OTelSpanKind.CLIENT,
190199
)
191200
finally:
201+
_inside_poolmanager.reset(pm_token)
192202
calling_library_context.reset(context_token)
193203

194204
module.PoolManager.urlopen = patched_urlopen
@@ -231,46 +241,36 @@ def patched_urlopen(
231241
port_str = f":{port}" if port and port not in (80, 443) else ""
232242
full_url = f"{scheme}://{host}{port_str}{url}"
233243

234-
# Pass through if SDK is disabled
244+
_passthrough = partial(
245+
original_urlopen,
246+
pool_self,
247+
method,
248+
url,
249+
body=body,
250+
headers=headers,
251+
retries=retries,
252+
redirect=redirect,
253+
assert_same_host=assert_same_host,
254+
timeout=timeout,
255+
pool_timeout=pool_timeout,
256+
release_conn=release_conn,
257+
chunked=chunked,
258+
body_pos=body_pos,
259+
preload_content=preload_content,
260+
decode_content=decode_content,
261+
**response_kw,
262+
)
263+
235264
if sdk.mode == TuskDriftMode.DISABLED:
236-
return original_urlopen(
237-
pool_self,
238-
method,
239-
url,
240-
body=body,
241-
headers=headers,
242-
retries=retries,
243-
redirect=redirect,
244-
assert_same_host=assert_same_host,
245-
timeout=timeout,
246-
pool_timeout=pool_timeout,
247-
release_conn=release_conn,
248-
chunked=chunked,
249-
body_pos=body_pos,
250-
preload_content=preload_content,
251-
decode_content=decode_content,
252-
**response_kw,
253-
)
265+
return _passthrough()
266+
267+
# PoolManager.urlopen already created the span for this request;
268+
# skip to avoid a duplicate child span.
269+
if _inside_poolmanager.get():
270+
return _passthrough()
254271

255272
if instrumentation_self._is_already_instrumented_by_higher_level():
256-
return original_urlopen(
257-
pool_self,
258-
method,
259-
url,
260-
body=body,
261-
headers=headers,
262-
retries=retries,
263-
redirect=redirect,
264-
assert_same_host=assert_same_host,
265-
timeout=timeout,
266-
pool_timeout=pool_timeout,
267-
release_conn=release_conn,
268-
chunked=chunked,
269-
body_pos=body_pos,
270-
preload_content=preload_content,
271-
decode_content=decode_content,
272-
**response_kw,
273-
)
273+
return _passthrough()
274274

275275
# Set calling_library_context to suppress socket instrumentation warnings
276276
# for internal socket calls made by urllib3

0 commit comments

Comments
 (0)