Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#3673](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3673))
- `opentelemetry-instrumentation-starlette`/`opentelemetry-instrumentation-fastapi`: Fixes a crash when host-based routing is used
([#3507](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3507))
- `opentelemetry-instrumentation-fastapi`: Fixes issue [#2808](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/2808)
([#3731](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3731))
- Fix documentation order of sections and headers for Django, Flask, MySQL, mysqlclient, psycopg, psycopg2, pymysql, sqlalchemy instrumentations.
([#3719](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3719))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,29 @@ class _DjangoMiddleware(MiddlewareMixin):
None
)

@staticmethod
def _format_request_objects_in_headers(attributes):
for key, value_list in list(attributes.items()):
new_values = []
for value in value_list:
if hasattr(value, "__class__"):
if value.__class__.__name__ in (
"HttpRequest",
"WSGIRequest",
):
try:
method = getattr(value, "method", "UNKNOWN")
path = getattr(value, "path", "UNKNOWN")
new_values.append(f"HttpRequest({method} {path})")
except (AttributeError, ValueError, TypeError):
new_values.append("HttpRequest(...)")
else:
new_values.append(value)
else:
new_values.append(value)
attributes[key] = new_values
return attributes

@staticmethod
def _get_span_name(request):
method = sanitize_method(request.method.strip())
Expand Down Expand Up @@ -276,6 +299,13 @@ def process_request(self, request):
custom_attributes = (
wsgi_collect_custom_request_headers_attributes(carrier)
)
# Process custom attributes to handle WSGIRequest objects
custom_attributes = (
self._format_request_objects_in_headers(
custom_attributes
)
)

if len(custom_attributes) > 0:
span.set_attributes(custom_attributes)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from unittest.mock import Mock, patch

from django import VERSION, conf
from django.core.handlers.wsgi import WSGIRequest
from django.http import HttpRequest, HttpResponse
from django.test.client import Client
from django.test.utils import setup_test_environment, teardown_test_environment
Expand Down Expand Up @@ -1019,6 +1020,117 @@ def tearDownClass(cls):
super().tearDownClass()
conf.settings = conf.LazySettings()

@staticmethod
def _format_request_objects_in_headers(attributes):
for key, value_list in list(attributes.items()):
new_values = []
for value in value_list:
if hasattr(value, "__class__"):
if value.__class__.__name__ in (
"HttpRequest",
"WSGIRequest",
):
try:
method = getattr(value, "method", "UNKNOWN")
request_path = getattr(value, "path", "UNKNOWN")
new_values.append(
f"HttpRequest({method} {request_path})"
)
except (AttributeError, ValueError, TypeError):
new_values.append("HttpRequest(...)")
else:
new_values.append(value)
else:
new_values.append(value)
attributes[key] = new_values
return attributes

def test_wsgi_request_in_header_is_properly_formatted(self):
mock_wsgi_request = Mock(spec=WSGIRequest)
mock_wsgi_request.method = "GET"
mock_wsgi_request.path = "/test/path"
mock_wsgi_request.__class__.__name__ = "WSGIRequest"

input_attributes = {
"http.request.header.test_wsgirequest_header": [mock_wsgi_request]
}
expected_attributes = {
"http.request.header.test_wsgirequest_header": [
"HttpRequest(GET /test/path)"
]
}

formatted_attributes = TestMiddlewareWsgiWithCustomHeaders._format_request_objects_in_headers(
input_attributes
)

self.assertEqual(formatted_attributes, expected_attributes)

def test_wsgi_request_handles_extraction_error(self):
mock_wsgi_request = Mock(spec=WSGIRequest)
mock_wsgi_request.__class__.__name__ = "WSGIRequest"

type(mock_wsgi_request).method = property(
lambda self: (_ for _ in ()).throw(ValueError("Test error"))
)

input_attributes = {
"http.request.header.test_wsgirequest_header": [mock_wsgi_request]
}
expected_attributes = {
"http.request.header.test_wsgirequest_header": ["HttpRequest(...)"]
}

formatted_attributes = TestMiddlewareWsgiWithCustomHeaders._format_request_objects_in_headers(
input_attributes
)

self.assertEqual(formatted_attributes, expected_attributes)

def test_handles_http_request_as_well(self):
mock_http_request = Mock(spec=HttpRequest)
mock_http_request.method = "POST"
mock_http_request.path = "/api/data"
mock_http_request.__class__.__name__ = "HttpRequest"

input_attributes = {
"http.request.header.test_httprequest_header": [mock_http_request]
}
expected_attributes = {
"http.request.header.test_httprequest_header": [
"HttpRequest(POST /api/data)"
]
}

formatted_attributes = TestMiddlewareWsgiWithCustomHeaders._format_request_objects_in_headers(
input_attributes
)

self.assertEqual(formatted_attributes, expected_attributes)

def test_regular_header_values_are_preserved(self):
mock_wsgi_request = Mock(spec=WSGIRequest)
mock_wsgi_request.method = "GET"
mock_wsgi_request.path = "/test/path"
mock_wsgi_request.__class__.__name__ = "WSGIRequest"

input_attributes = {
"http.request.header.test_wsgirequest_header": [mock_wsgi_request],
"http.request.header.test_regular_header": ["regular-value"],
}
expected_attributes = {
"http.request.header.test_wsgirequest_header": [
"HttpRequest(GET /test/path)"
],
"http.request.header.test_regular_header": ["regular-value"],
}

formatted_attributes = TestMiddlewareWsgiWithCustomHeaders._format_request_objects_in_headers(
input_attributes
)

self.assertEqual(formatted_attributes, expected_attributes)

def test_http_custom_request_headers_in_span_attributes(self):
expected = {
"http.request.header.custom_test_header_1": (
Expand Down