[opentelemetry-instrumentation-django] Handle exceptions from request/response hooks (#2153)

This commit is contained in:
Eddie Bracho
2024-07-02 11:41:55 -07:00
committed by GitHub
parent 77749225b7
commit b16394b202
3 changed files with 46 additions and 7 deletions

View File

@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
- `opentelemetry-instrumentation-django` Handle exceptions from request/response hooks
([#2153](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2153))
- `opentelemetry-instrumentation-asyncio` instrumented `asyncio.wait_for` properly raises `asyncio.TimeoutError` as expected
([#2637](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2637))
- `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
([#2136](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2136))
- `opentelemetry-resource-detector-azure` Suppress instrumentation for `urllib` call
([#2178](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2178))
- AwsLambdaInstrumentor handles and re-raises function exception ([#2245](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2245))
- AwsLambdaInstrumentor handles and re-raises function exception
([#2245](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2245))
### Added

View File

@ -187,6 +187,7 @@ class _DjangoMiddleware(MiddlewareMixin):
return request.method
# pylint: disable=too-many-locals
# pylint: disable=too-many-branches
def process_request(self, request):
# request.META is a dictionary containing all available HTTP headers
# Read more about request.META here:
@ -286,9 +287,14 @@ class _DjangoMiddleware(MiddlewareMixin):
request.META[self._environ_token] = token
if _DjangoMiddleware._otel_request_hook:
_DjangoMiddleware._otel_request_hook( # pylint: disable=not-callable
span, request
)
try:
_DjangoMiddleware._otel_request_hook( # pylint: disable=not-callable
span, request
)
except Exception: # pylint: disable=broad-exception-caught
# Raising an exception here would leak the request span since process_response
# would not be called. Log the exception instead.
_logger.exception("Exception raised by request_hook")
# pylint: disable=unused-argument
def process_view(self, request, view_func, *args, **kwargs):
@ -385,10 +391,14 @@ class _DjangoMiddleware(MiddlewareMixin):
# record any exceptions raised while processing the request
exception = request.META.pop(self._environ_exception_key, None)
if _DjangoMiddleware._otel_response_hook:
_DjangoMiddleware._otel_response_hook( # pylint: disable=not-callable
span, request, response
)
try:
_DjangoMiddleware._otel_response_hook( # pylint: disable=not-callable
span, request, response
)
except Exception: # pylint: disable=broad-exception-caught
_logger.exception("Exception raised by response_hook")
if exception:
activation.__exit__(

View File

@ -392,6 +392,32 @@ class TestMiddleware(WsgiTestBase):
self.assertIsInstance(response_hook_args[2], HttpResponse)
self.assertEqual(response_hook_args[2], response)
def test_request_hook_exception(self):
def request_hook(span, request):
# pylint: disable=broad-exception-raised
raise Exception("request hook exception")
_DjangoMiddleware._otel_request_hook = request_hook
Client().get("/span_name/1234/")
_DjangoMiddleware._otel_request_hook = None
# ensure that span ended
finished_spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(finished_spans), 1)
def test_response_hook_exception(self):
def response_hook(span, request, response):
# pylint: disable=broad-exception-raised
raise Exception("response hook exception")
_DjangoMiddleware._otel_response_hook = response_hook
Client().get("/span_name/1234/")
_DjangoMiddleware._otel_response_hook = None
# ensure that span ended
finished_spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(finished_spans), 1)
def test_trace_parent(self):
id_generator = RandomIdGenerator()
trace_id = format_trace_id(id_generator.generate_trace_id())