feat(urllib3)!: refactor request hook parameters (#2711)

This commit is contained in:
Pavel Perestoronin 2024-07-26 00:08:26 +02:00 committed by GitHub
parent 6690ecc441
commit 4f985196c6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 67 additions and 18 deletions

View File

@ -67,6 +67,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#2726](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2726))
- `opentelemetry-instrumentation-fastapi` Add dependency support for fastapi-slim
([#2702](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2702))
- `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))
### Fixed

View File

@ -47,14 +47,19 @@ The hooks can be configured as follows:
.. code:: python
# `request` is an instance of urllib3.connectionpool.HTTPConnectionPool
def request_hook(span, request):
pass
def request_hook(
span: Span,
pool: urllib3.connectionpool.HTTPConnectionPool,
request_info: RequestInfo,
) -> Any:
...
# `request` is an instance of urllib3.connectionpool.HTTPConnectionPool
# `response` is an instance of urllib3.response.HTTPResponse
def response_hook(span, request, response):
pass
def response_hook(
span: Span,
pool: urllib3.connectionpool.HTTPConnectionPool,
response: urllib3.response.HTTPResponse,
) -> Any:
...
URLLib3Instrumentor().instrument(
request_hook=request_hook, response_hook=response_hook
@ -81,6 +86,7 @@ API
import collections.abc
import io
import typing
from dataclasses import dataclass
from timeit import default_timer
from typing import Collection
@ -135,14 +141,29 @@ from opentelemetry.util.http.httplib import set_ip_on_next_http_connection
_excluded_urls_from_env = get_excluded_urls("URLLIB3")
@dataclass
class RequestInfo:
"""Arguments that were passed to the ``urlopen()`` call."""
__slots__ = ("method", "url", "headers", "body")
# The type annotations here come from ``HTTPConnectionPool.urlopen()``.
method: str
url: str
headers: typing.Optional[typing.Mapping[str, str]]
body: typing.Union[
bytes, typing.IO[typing.Any], typing.Iterable[bytes], str, None
]
_UrlFilterT = typing.Optional[typing.Callable[[str], str]]
_RequestHookT = typing.Optional[
typing.Callable[
[
Span,
urllib3.connectionpool.HTTPConnectionPool,
typing.Dict,
typing.Optional[str],
RequestInfo,
],
None,
]
@ -317,7 +338,16 @@ def _instrument(
span_name, kind=SpanKind.CLIENT, attributes=span_attributes
) as span, set_ip_on_next_http_connection(span):
if callable(request_hook):
request_hook(span, instance, headers, body)
request_hook(
span,
instance,
RequestInfo(
method=method,
url=url,
headers=headers,
body=body,
),
)
inject(headers)
# TODO: add error handling to also set exception `error.type` in new semconv
with suppress_http_instrumentation():

View File

@ -27,7 +27,10 @@ from opentelemetry.instrumentation._semconv import (
_HTTPStabilityMode,
_OpenTelemetrySemanticConventionStability,
)
from opentelemetry.instrumentation.urllib3 import URLLib3Instrumentor
from opentelemetry.instrumentation.urllib3 import (
RequestInfo,
URLLib3Instrumentor,
)
from opentelemetry.instrumentation.utils import (
suppress_http_instrumentation,
suppress_instrumentation,
@ -42,6 +45,7 @@ from opentelemetry.semconv.attributes.url_attributes import URL_FULL
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.test.mock_textmap import MockTextMapPropagator
from opentelemetry.test.test_base import TestBase
from opentelemetry.trace import Span
from opentelemetry.util.http import get_excluded_urls
# pylint: disable=too-many-public-methods
@ -521,10 +525,10 @@ class TestURLLib3Instrumentor(TestBase):
self.assert_success_span(response, self.HTTP_URL)
def test_hooks(self):
def request_hook(span, request, body, headers):
def request_hook(span, pool, request_info):
span.update_name("name set from hook")
def response_hook(span, request, response):
def response_hook(span, pool, response):
span.set_attribute("response_hook_attr", "value")
URLLib3Instrumentor().uninstrument()
@ -541,11 +545,17 @@ class TestURLLib3Instrumentor(TestBase):
self.assertEqual(span.attributes["response_hook_attr"], "value")
def test_request_hook_params(self):
def request_hook(span, request, headers, body):
def request_hook(
span: Span,
_pool: urllib3.connectionpool.ConnectionPool,
request_info: RequestInfo,
) -> None:
span.set_attribute("request_hook_method", request_info.method)
span.set_attribute("request_hook_url", request_info.url)
span.set_attribute(
"request_hook_headers", json.dumps(dict(headers))
"request_hook_headers", json.dumps(dict(request_info.headers))
)
span.set_attribute("request_hook_body", body)
span.set_attribute("request_hook_body", request_info.body)
URLLib3Instrumentor().uninstrument()
URLLib3Instrumentor().instrument(
@ -564,6 +574,10 @@ class TestURLLib3Instrumentor(TestBase):
span = self.assert_span()
self.assertEqual(span.attributes["request_hook_method"], "POST")
self.assertEqual(
span.attributes["request_hook_url"], "http://mock/status/200"
)
self.assertIn("request_hook_headers", span.attributes)
self.assertEqual(
span.attributes["request_hook_headers"], json.dumps(headers)
@ -572,8 +586,12 @@ class TestURLLib3Instrumentor(TestBase):
self.assertEqual(span.attributes["request_hook_body"], body)
def test_request_positional_body(self):
def request_hook(span, request, headers, body):
span.set_attribute("request_hook_body", body)
def request_hook(
span: Span,
_pool: urllib3.connectionpool.ConnectionPool,
request_info: RequestInfo,
) -> None:
span.set_attribute("request_hook_body", request_info.body)
URLLib3Instrumentor().uninstrument()
URLLib3Instrumentor().instrument(