Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Urllib3 request hook #660

Merged
merged 9 commits into from
Sep 13, 2021
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased](https:/open-telemetry/opentelemetry-python/compare/v1.5.0-0.24b0...HEAD)

### Changed

- `opentelemetry-instrumentation-urllib3` Added `_ExtendedRequestHookT` as an optional type of `request_hook`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this description still accurate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, it wasn't, pushed an update to it just now

this type extends the existing `_RequestHookT` with two more fields - the request body and the request headers

## [1.5.0-0.24b0](https:/open-telemetry/opentelemetry-python/releases/tag/v1.5.0-0.24b0) - 2021-08-26

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,15 @@ def response_hook(span, request, response):

_UrlFilterT = typing.Optional[typing.Callable[[str], str]]
_RequestHookT = typing.Optional[
typing.Callable[[Span, urllib3.connectionpool.HTTPConnectionPool], None]
typing.Callable[
[
Span,
urllib3.connectionpool.HTTPConnectionPool,
typing.Dict,
typing.Optional[str],
],
None,
]
]
_ResponseHookT = typing.Optional[
typing.Callable[
Expand Down Expand Up @@ -150,6 +158,7 @@ def instrumented_urlopen(wrapped, instance, args, kwargs):
method = _get_url_open_arg("method", args, kwargs).upper()
url = _get_url(instance, args, kwargs, url_filter)
headers = _prepare_headers(kwargs)
body = _get_url_open_arg("body", args, kwargs)

span_name = "HTTP {}".format(method.strip())
span_attributes = {
Expand All @@ -161,7 +170,7 @@ def instrumented_urlopen(wrapped, instance, args, kwargs):
span_name, kind=SpanKind.CLIENT, attributes=span_attributes
) as span:
if callable(request_hook):
request_hook(span, instance)
request_hook(span, instance, headers, body)
inject(headers)

with _suppress_further_instrumentation():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import json
import typing
from unittest import mock

Expand Down Expand Up @@ -45,6 +45,7 @@ def setUp(self):
httpretty.enable(allow_net_connect=False)
httpretty.register_uri(httpretty.GET, self.HTTP_URL, body="Hello!")
httpretty.register_uri(httpretty.GET, self.HTTPS_URL, body="Hello!")
httpretty.register_uri(httpretty.POST, self.HTTP_URL, body="Hello!")

def tearDown(self):
super().tearDown()
Expand Down Expand Up @@ -261,7 +262,7 @@ def test_credential_removal(self):
self.assert_success_span(response, self.HTTP_URL)

def test_hooks(self):
def request_hook(span, request):
def request_hook(span, request, body, headers):
span.update_name("name set from hook")

def response_hook(span, request, response):
Expand All @@ -279,3 +280,30 @@ def response_hook(span, request, response):
self.assertEqual(span.name, "name set from hook")
self.assertIn("response_hook_attr", span.attributes)
self.assertEqual(span.attributes["response_hook_attr"], "value")

def test_request_hook_params(self):
def request_hook(span, request, headers, body):
span.set_attribute("request_hook_headers", json.dumps(headers))
span.set_attribute("request_hook_body", body)

URLLib3Instrumentor().uninstrument()
URLLib3Instrumentor().instrument(request_hook=request_hook,)

headers = {"header1": "value1", "header2": "value2"}
body = "param1=1&param2=2"

pool = urllib3.HTTPConnectionPool("httpbin.org")
response = pool.request(
"POST", "/status/200", body=body, headers=headers
)

self.assertEqual(b"Hello!", response.data)

span = self.assert_span()

self.assertIn("request_hook_headers", span.attributes)
self.assertEqual(
span.attributes["request_hook_headers"], json.dumps(headers)
)
self.assertIn("request_hook_body", span.attributes)
self.assertEqual(span.attributes["request_hook_body"], body)