Skip to content

Commit ff5a3df

Browse files
committed
feat(urllib3)!: refactor request hook parameters
1 parent a322a0a commit ff5a3df

File tree

3 files changed

+67
-21
lines changed

3 files changed

+67
-21
lines changed

Diff for: CHANGELOG.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4646
([#2580](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2580))
4747
- Populate `{method}` as `HTTP` on `_OTHER` methods from scope for `asgi` middleware
4848
([#2610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2610))
49-
- Populate `{method}` as `HTTP` on `_OTHER` methods from scope for `fastapi` middleware
50-
([#2682](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2682))
49+
- Populate `{method}` as `HTTP` on `_OTHER` methods from scope for `fastapi` middleware
50+
([#2682](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2682))
51+
- `urllib3`: the request hook has been refactored: the `headers` and `body` parameters have been replaced by a single `request_info: RequestInfo` parameter that now contains `method` and `url` as well ([#2711](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2711))
5152

5253
### Fixed
5354
- Handle `redis.exceptions.WatchError` as a non-error event in redis instrumentation

Diff for: instrumentation/opentelemetry-instrumentation-urllib3/src/opentelemetry/instrumentation/urllib3/__init__.py

+38-11
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,19 @@ def strip_query_params(url: str) -> str:
4747
4848
.. code:: python
4949
50-
# `request` is an instance of urllib3.connectionpool.HTTPConnectionPool
51-
def request_hook(span, request):
52-
pass
53-
54-
# `request` is an instance of urllib3.connectionpool.HTTPConnectionPool
55-
# `response` is an instance of urllib3.response.HTTPResponse
56-
def response_hook(span, request, response):
57-
pass
50+
def request_hook(
51+
span: Span,
52+
pool: urllib3.connectionpool.HTTPConnectionPool,
53+
request_info: RequestInfo,
54+
) -> Any:
55+
...
56+
57+
def response_hook(
58+
span: Span,
59+
pool: urllib3.connectionpool.HTTPConnectionPool,
60+
response: urllib3.response.HTTPResponse,
61+
) -> Any:
62+
...
5863
5964
URLLib3Instrumentor().instrument(
6065
request_hook=request_hook, response_hook=response_hook
@@ -81,6 +86,7 @@ def response_hook(span, request, response):
8186
import collections.abc
8287
import io
8388
import typing
89+
from dataclasses import dataclass
8490
from timeit import default_timer
8591
from typing import Collection
8692

@@ -111,14 +117,29 @@ def response_hook(span, request, response):
111117

112118
_excluded_urls_from_env = get_excluded_urls("URLLIB3")
113119

120+
121+
@dataclass
122+
class RequestInfo:
123+
"""Arguments that were passed to the ``urlopen()`` call."""
124+
125+
__slots__ = ("method", "url", "headers", "body")
126+
127+
# The type annotations here come from ``HTTPConnectionPool.urlopen()``.
128+
method: str
129+
url: str
130+
headers: typing.Optional[typing.Mapping[str, str]]
131+
body: typing.Union[
132+
bytes, typing.IO[typing.Any], typing.Iterable[bytes], str, None
133+
]
134+
135+
114136
_UrlFilterT = typing.Optional[typing.Callable[[str], str]]
115137
_RequestHookT = typing.Optional[
116138
typing.Callable[
117139
[
118140
Span,
119141
urllib3.connectionpool.HTTPConnectionPool,
120-
typing.Dict,
121-
typing.Optional[str],
142+
RequestInfo,
122143
],
123144
None,
124145
]
@@ -243,7 +264,13 @@ def instrumented_urlopen(wrapped, instance, args, kwargs):
243264
span_name, kind=SpanKind.CLIENT, attributes=span_attributes
244265
) as span, set_ip_on_next_http_connection(span):
245266
if callable(request_hook):
246-
request_hook(span, instance, headers, body)
267+
request_info = RequestInfo(
268+
method=method,
269+
url=url,
270+
headers=headers,
271+
body=body,
272+
)
273+
request_hook(span, instance, request_info)
247274
inject(headers)
248275

249276
with suppress_http_instrumentation():

Diff for: instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_integration.py

+26-8
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
import urllib3.exceptions
2121

2222
from opentelemetry import trace
23-
from opentelemetry.instrumentation.urllib3 import URLLib3Instrumentor
23+
from opentelemetry.instrumentation.urllib3 import (
24+
RequestInfo,
25+
URLLib3Instrumentor,
26+
)
2427
from opentelemetry.instrumentation.utils import (
2528
suppress_http_instrumentation,
2629
suppress_instrumentation,
@@ -29,6 +32,7 @@
2932
from opentelemetry.semconv.trace import SpanAttributes
3033
from opentelemetry.test.mock_textmap import MockTextMapPropagator
3134
from opentelemetry.test.test_base import TestBase
35+
from opentelemetry.trace import Span
3236
from opentelemetry.util.http import get_excluded_urls
3337

3438
# pylint: disable=too-many-public-methods
@@ -315,10 +319,10 @@ def test_credential_removal(self):
315319
self.assert_success_span(response, self.HTTP_URL)
316320

317321
def test_hooks(self):
318-
def request_hook(span, request, body, headers):
322+
def request_hook(span, pool, request_info):
319323
span.update_name("name set from hook")
320324

321-
def response_hook(span, request, response):
325+
def response_hook(span, pool, response):
322326
span.set_attribute("response_hook_attr", "value")
323327

324328
URLLib3Instrumentor().uninstrument()
@@ -335,11 +339,17 @@ def response_hook(span, request, response):
335339
self.assertEqual(span.attributes["response_hook_attr"], "value")
336340

337341
def test_request_hook_params(self):
338-
def request_hook(span, request, headers, body):
342+
def request_hook(
343+
span: Span,
344+
_pool: urllib3.connectionpool.ConnectionPool,
345+
request_info: RequestInfo,
346+
) -> None:
347+
span.set_attribute("request_hook_method", request_info.method)
348+
span.set_attribute("request_hook_url", request_info.url)
339349
span.set_attribute(
340-
"request_hook_headers", json.dumps(dict(headers))
350+
"request_hook_headers", json.dumps(dict(request_info.headers))
341351
)
342-
span.set_attribute("request_hook_body", body)
352+
span.set_attribute("request_hook_body", request_info.body)
343353

344354
URLLib3Instrumentor().uninstrument()
345355
URLLib3Instrumentor().instrument(
@@ -358,6 +368,10 @@ def request_hook(span, request, headers, body):
358368

359369
span = self.assert_span()
360370

371+
self.assertEqual(span.attributes["request_hook_method"], "POST")
372+
self.assertEqual(
373+
span.attributes["request_hook_url"], "http://mock/status/200"
374+
)
361375
self.assertIn("request_hook_headers", span.attributes)
362376
self.assertEqual(
363377
span.attributes["request_hook_headers"], json.dumps(headers)
@@ -366,8 +380,12 @@ def request_hook(span, request, headers, body):
366380
self.assertEqual(span.attributes["request_hook_body"], body)
367381

368382
def test_request_positional_body(self):
369-
def request_hook(span, request, headers, body):
370-
span.set_attribute("request_hook_body", body)
383+
def request_hook(
384+
span: Span,
385+
_pool: urllib3.connectionpool.ConnectionPool,
386+
request_info: RequestInfo,
387+
) -> None:
388+
span.set_attribute("request_hook_body", request_info.body)
371389

372390
URLLib3Instrumentor().uninstrument()
373391
URLLib3Instrumentor().instrument(

0 commit comments

Comments
 (0)