Skip to content

Commit b16394b

Browse files
authored
[opentelemetry-instrumentation-django] Handle exceptions from request/response hooks (open-telemetry#2153)
1 parent 7774922 commit b16394b

File tree

3 files changed

+46
-7
lines changed

3 files changed

+46
-7
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## Unreleased
99

10+
- `opentelemetry-instrumentation-django` Handle exceptions from request/response hooks
11+
([#2153](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2153))
1012
- `opentelemetry-instrumentation-asyncio` instrumented `asyncio.wait_for` properly raises `asyncio.TimeoutError` as expected
1113
([#2637](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2637))
1214
- `opentelemetry-instrumentation-aws-lambda` Bugfix: AWS Lambda event source key incorrect for SNS in instrumentation library.
@@ -151,7 +153,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
151153
([#2136](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2136))
152154
- `opentelemetry-resource-detector-azure` Suppress instrumentation for `urllib` call
153155
([#2178](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2178))
154-
- AwsLambdaInstrumentor handles and re-raises function exception ([#2245](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2245))
156+
- AwsLambdaInstrumentor handles and re-raises function exception
157+
([#2245](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2245))
155158

156159
### Added
157160

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

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ def _get_span_name(request):
187187
return request.method
188188

189189
# pylint: disable=too-many-locals
190+
# pylint: disable=too-many-branches
190191
def process_request(self, request):
191192
# request.META is a dictionary containing all available HTTP headers
192193
# Read more about request.META here:
@@ -286,9 +287,14 @@ def process_request(self, request):
286287
request.META[self._environ_token] = token
287288

288289
if _DjangoMiddleware._otel_request_hook:
289-
_DjangoMiddleware._otel_request_hook( # pylint: disable=not-callable
290-
span, request
291-
)
290+
try:
291+
_DjangoMiddleware._otel_request_hook( # pylint: disable=not-callable
292+
span, request
293+
)
294+
except Exception: # pylint: disable=broad-exception-caught
295+
# Raising an exception here would leak the request span since process_response
296+
# would not be called. Log the exception instead.
297+
_logger.exception("Exception raised by request_hook")
292298

293299
# pylint: disable=unused-argument
294300
def process_view(self, request, view_func, *args, **kwargs):
@@ -385,10 +391,14 @@ def process_response(self, request, response):
385391

386392
# record any exceptions raised while processing the request
387393
exception = request.META.pop(self._environ_exception_key, None)
394+
388395
if _DjangoMiddleware._otel_response_hook:
389-
_DjangoMiddleware._otel_response_hook( # pylint: disable=not-callable
390-
span, request, response
391-
)
396+
try:
397+
_DjangoMiddleware._otel_response_hook( # pylint: disable=not-callable
398+
span, request, response
399+
)
400+
except Exception: # pylint: disable=broad-exception-caught
401+
_logger.exception("Exception raised by response_hook")
392402

393403
if exception:
394404
activation.__exit__(

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,32 @@ def response_hook(span, request, response):
392392
self.assertIsInstance(response_hook_args[2], HttpResponse)
393393
self.assertEqual(response_hook_args[2], response)
394394

395+
def test_request_hook_exception(self):
396+
def request_hook(span, request):
397+
# pylint: disable=broad-exception-raised
398+
raise Exception("request hook exception")
399+
400+
_DjangoMiddleware._otel_request_hook = request_hook
401+
Client().get("/span_name/1234/")
402+
_DjangoMiddleware._otel_request_hook = None
403+
404+
# ensure that span ended
405+
finished_spans = self.memory_exporter.get_finished_spans()
406+
self.assertEqual(len(finished_spans), 1)
407+
408+
def test_response_hook_exception(self):
409+
def response_hook(span, request, response):
410+
# pylint: disable=broad-exception-raised
411+
raise Exception("response hook exception")
412+
413+
_DjangoMiddleware._otel_response_hook = response_hook
414+
Client().get("/span_name/1234/")
415+
_DjangoMiddleware._otel_response_hook = None
416+
417+
# ensure that span ended
418+
finished_spans = self.memory_exporter.get_finished_spans()
419+
self.assertEqual(len(finished_spans), 1)
420+
395421
def test_trace_parent(self):
396422
id_generator = RandomIdGenerator()
397423
trace_id = format_trace_id(id_generator.generate_trace_id())

0 commit comments

Comments
 (0)