Skip to content

Commit 4f98519

Browse files
authored
feat(urllib3)!: refactor request hook parameters (#2711)
1 parent 6690ecc commit 4f98519

File tree

3 files changed

+68
-19
lines changed

3 files changed

+68
-19
lines changed

Diff for: CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6767
([#2726](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2726))
6868
- `opentelemetry-instrumentation-fastapi` Add dependency support for fastapi-slim
6969
([#2702](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2702))
70+
- `opentelemetry-instrumentation-urllib3` improve request_hook, replacing `headers` and `body` parameters with a single `request_info: RequestInfo` parameter that now contains the `method` and `url` ([#2711](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2711))
7071

7172
### Fixed
7273

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

+41-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

@@ -135,14 +141,29 @@ def response_hook(span, request, response):
135141

136142
_excluded_urls_from_env = get_excluded_urls("URLLIB3")
137143

144+
145+
@dataclass
146+
class RequestInfo:
147+
"""Arguments that were passed to the ``urlopen()`` call."""
148+
149+
__slots__ = ("method", "url", "headers", "body")
150+
151+
# The type annotations here come from ``HTTPConnectionPool.urlopen()``.
152+
method: str
153+
url: str
154+
headers: typing.Optional[typing.Mapping[str, str]]
155+
body: typing.Union[
156+
bytes, typing.IO[typing.Any], typing.Iterable[bytes], str, None
157+
]
158+
159+
138160
_UrlFilterT = typing.Optional[typing.Callable[[str], str]]
139161
_RequestHookT = typing.Optional[
140162
typing.Callable[
141163
[
142164
Span,
143165
urllib3.connectionpool.HTTPConnectionPool,
144-
typing.Dict,
145-
typing.Optional[str],
166+
RequestInfo,
146167
],
147168
None,
148169
]
@@ -317,7 +338,16 @@ def instrumented_urlopen(wrapped, instance, args, kwargs):
317338
span_name, kind=SpanKind.CLIENT, attributes=span_attributes
318339
) as span, set_ip_on_next_http_connection(span):
319340
if callable(request_hook):
320-
request_hook(span, instance, headers, body)
341+
request_hook(
342+
span,
343+
instance,
344+
RequestInfo(
345+
method=method,
346+
url=url,
347+
headers=headers,
348+
body=body,
349+
),
350+
)
321351
inject(headers)
322352
# TODO: add error handling to also set exception `error.type` in new semconv
323353
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
@@ -27,7 +27,10 @@
2727
_HTTPStabilityMode,
2828
_OpenTelemetrySemanticConventionStability,
2929
)
30-
from opentelemetry.instrumentation.urllib3 import URLLib3Instrumentor
30+
from opentelemetry.instrumentation.urllib3 import (
31+
RequestInfo,
32+
URLLib3Instrumentor,
33+
)
3134
from opentelemetry.instrumentation.utils import (
3235
suppress_http_instrumentation,
3336
suppress_instrumentation,
@@ -42,6 +45,7 @@
4245
from opentelemetry.semconv.trace import SpanAttributes
4346
from opentelemetry.test.mock_textmap import MockTextMapPropagator
4447
from opentelemetry.test.test_base import TestBase
48+
from opentelemetry.trace import Span
4549
from opentelemetry.util.http import get_excluded_urls
4650

4751
# pylint: disable=too-many-public-methods
@@ -521,10 +525,10 @@ def test_credential_removal(self):
521525
self.assert_success_span(response, self.HTTP_URL)
522526

523527
def test_hooks(self):
524-
def request_hook(span, request, body, headers):
528+
def request_hook(span, pool, request_info):
525529
span.update_name("name set from hook")
526530

527-
def response_hook(span, request, response):
531+
def response_hook(span, pool, response):
528532
span.set_attribute("response_hook_attr", "value")
529533

530534
URLLib3Instrumentor().uninstrument()
@@ -541,11 +545,17 @@ def response_hook(span, request, response):
541545
self.assertEqual(span.attributes["response_hook_attr"], "value")
542546

543547
def test_request_hook_params(self):
544-
def request_hook(span, request, headers, body):
548+
def request_hook(
549+
span: Span,
550+
_pool: urllib3.connectionpool.ConnectionPool,
551+
request_info: RequestInfo,
552+
) -> None:
553+
span.set_attribute("request_hook_method", request_info.method)
554+
span.set_attribute("request_hook_url", request_info.url)
545555
span.set_attribute(
546-
"request_hook_headers", json.dumps(dict(headers))
556+
"request_hook_headers", json.dumps(dict(request_info.headers))
547557
)
548-
span.set_attribute("request_hook_body", body)
558+
span.set_attribute("request_hook_body", request_info.body)
549559

550560
URLLib3Instrumentor().uninstrument()
551561
URLLib3Instrumentor().instrument(
@@ -564,6 +574,10 @@ def request_hook(span, request, headers, body):
564574

565575
span = self.assert_span()
566576

577+
self.assertEqual(span.attributes["request_hook_method"], "POST")
578+
self.assertEqual(
579+
span.attributes["request_hook_url"], "http://mock/status/200"
580+
)
567581
self.assertIn("request_hook_headers", span.attributes)
568582
self.assertEqual(
569583
span.attributes["request_hook_headers"], json.dumps(headers)
@@ -572,8 +586,12 @@ def request_hook(span, request, headers, body):
572586
self.assertEqual(span.attributes["request_hook_body"], body)
573587

574588
def test_request_positional_body(self):
575-
def request_hook(span, request, headers, body):
576-
span.set_attribute("request_hook_body", body)
589+
def request_hook(
590+
span: Span,
591+
_pool: urllib3.connectionpool.ConnectionPool,
592+
request_info: RequestInfo,
593+
) -> None:
594+
span.set_attribute("request_hook_body", request_info.body)
577595

578596
URLLib3Instrumentor().uninstrument()
579597
URLLib3Instrumentor().instrument(

0 commit comments

Comments
 (0)