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 @@ -15,6 +15,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `opentelemetry-instrumentation-botocore` Fix span injection for lambda invoke
([#663](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/663))

### Changed

- `opentelemetry-instrumentation-urllib3` Updated `_RequestHookT` with two additional fields - the request body and the request headers
([#660](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/660))

## [1.5.0-0.24b0](https://github.com/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)