Skip to content

Commit 369bd28

Browse files
committed
fix-for-issue-2808
1 parent 86d26ce commit 369bd28

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed

instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware/otel_middleware.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,26 @@ class _DjangoMiddleware(MiddlewareMixin):
165165
None
166166
)
167167

168+
@staticmethod
169+
def _format_request_objects_in_headers(attributes):
170+
for key, value_list in list(attributes.items()):
171+
new_values = []
172+
for value in value_list:
173+
if hasattr(value, '__class__'):
174+
if value.__class__.__name__ in ('HttpRequest', 'WSGIRequest'):
175+
try:
176+
method = getattr(value, 'method', 'UNKNOWN')
177+
path = getattr(value, 'path', 'UNKNOWN')
178+
new_values.append(f"HttpRequest({method} {path})")
179+
except (AttributeError, ValueError, TypeError):
180+
new_values.append("HttpRequest(...)")
181+
else:
182+
new_values.append(value)
183+
else:
184+
new_values.append(value)
185+
attributes[key] = new_values
186+
return attributes
187+
168188
@staticmethod
169189
def _get_span_name(request):
170190
method = sanitize_method(request.method.strip())
@@ -276,6 +296,9 @@ def process_request(self, request):
276296
custom_attributes = (
277297
wsgi_collect_custom_request_headers_attributes(carrier)
278298
)
299+
# Process custom attributes to handle WSGIRequest objects
300+
custom_attributes = self._format_request_objects_in_headers(custom_attributes)
301+
279302
if len(custom_attributes) > 0:
280303
span.set_attributes(custom_attributes)
281304

instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from django.http import HttpRequest, HttpResponse
2424
from django.test.client import Client
2525
from django.test.utils import setup_test_environment, teardown_test_environment
26+
from django.core.handlers.wsgi import WSGIRequest
2627

2728
from opentelemetry import trace
2829
from opentelemetry.instrumentation._semconv import (
@@ -1019,6 +1020,84 @@ def tearDownClass(cls):
10191020
super().tearDownClass()
10201021
conf.settings = conf.LazySettings()
10211022

1023+
@staticmethod
1024+
def _format_request_objects_in_headers(attributes):
1025+
for key, value_list in list(attributes.items()):
1026+
new_values = []
1027+
for value in value_list:
1028+
if hasattr(value, '__class__'):
1029+
if value.__class__.__name__ in ('HttpRequest', 'WSGIRequest'):
1030+
try:
1031+
method = getattr(value, 'method', 'UNKNOWN')
1032+
path = getattr(value, 'path', 'UNKNOWN')
1033+
new_values.append(f"HttpRequest({method} {path})")
1034+
except (AttributeError, ValueError, TypeError):
1035+
new_values.append("HttpRequest(...)")
1036+
else:
1037+
new_values.append(value)
1038+
else:
1039+
new_values.append(value)
1040+
attributes[key] = new_values
1041+
return attributes
1042+
1043+
def test_wsgi_request_in_header_is_properly_formatted(self):
1044+
mock_wsgi_request = Mock(spec=WSGIRequest)
1045+
mock_wsgi_request.method = "GET"
1046+
mock_wsgi_request.path = "/test/path"
1047+
mock_wsgi_request.__class__.__name__ = "WSGIRequest"
1048+
1049+
input_attributes = {"http.request.header.test_wsgirequest_header": [mock_wsgi_request]}
1050+
expected_attributes = {"http.request.header.test_wsgirequest_header": ["HttpRequest(GET /test/path)"]}
1051+
1052+
formatted_attributes = TestMiddlewareWsgiWithCustomHeaders._format_request_objects_in_headers(input_attributes)
1053+
1054+
self.assertEqual(formatted_attributes, expected_attributes)
1055+
1056+
def test_wsgi_request_handles_extraction_error(self):
1057+
mock_wsgi_request = Mock(spec=WSGIRequest)
1058+
mock_wsgi_request.__class__.__name__ = "WSGIRequest"
1059+
1060+
type(mock_wsgi_request).method = property(lambda self: (_ for _ in ()).throw(ValueError("Test error")))
1061+
1062+
input_attributes = {"http.request.header.test_wsgirequest_header": [mock_wsgi_request]}
1063+
expected_attributes = {"http.request.header.test_wsgirequest_header": ["HttpRequest(...)"]}
1064+
1065+
formatted_attributes = TestMiddlewareWsgiWithCustomHeaders._format_request_objects_in_headers(input_attributes)
1066+
1067+
self.assertEqual(formatted_attributes, expected_attributes)
1068+
1069+
def test_handles_http_request_as_well(self):
1070+
mock_http_request = Mock(spec=HttpRequest)
1071+
mock_http_request.method = "POST"
1072+
mock_http_request.path = "/api/data"
1073+
mock_http_request.__class__.__name__ = "HttpRequest"
1074+
1075+
input_attributes = {"http.request.header.test_httprequest_header": [mock_http_request]}
1076+
expected_attributes = {"http.request.header.test_httprequest_header": ["HttpRequest(POST /api/data)"]}
1077+
1078+
formatted_attributes = TestMiddlewareWsgiWithCustomHeaders._format_request_objects_in_headers(input_attributes)
1079+
1080+
self.assertEqual(formatted_attributes, expected_attributes)
1081+
1082+
def test_regular_header_values_are_preserved(self):
1083+
mock_wsgi_request = Mock(spec=WSGIRequest)
1084+
mock_wsgi_request.method = "GET"
1085+
mock_wsgi_request.path = "/test/path"
1086+
mock_wsgi_request.__class__.__name__ = "WSGIRequest"
1087+
1088+
input_attributes = {
1089+
"http.request.header.test_wsgirequest_header": [mock_wsgi_request],
1090+
"http.request.header.test_regular_header": ["regular-value"]
1091+
}
1092+
expected_attributes = {
1093+
"http.request.header.test_wsgirequest_header": ["HttpRequest(GET /test/path)"],
1094+
"http.request.header.test_regular_header": ["regular-value"]
1095+
}
1096+
1097+
formatted_attributes = TestMiddlewareWsgiWithCustomHeaders._format_request_objects_in_headers(input_attributes)
1098+
1099+
self.assertEqual(formatted_attributes, expected_attributes)
1100+
10221101
def test_http_custom_request_headers_in_span_attributes(self):
10231102
expected = {
10241103
"http.request.header.custom_test_header_1": (

0 commit comments

Comments
 (0)