From ea873638eede73d650ad35c6464a70b9fa39fcad Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Thu, 15 Feb 2024 15:33:13 -0800 Subject: [PATCH 1/6] AwsInstrumentor catches handler exception --- .../instrumentation/aws_lambda/__init__.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py index 391bc32f60..ae04b63041 100644 --- a/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py @@ -97,6 +97,7 @@ def custom_event_context_extractor(lambda_event): get_tracer_provider, ) from opentelemetry.trace.propagation import get_current_span +from opentelemetry.trace.status import Status, StatusCode logger = logging.getLogger(__name__) @@ -350,7 +351,12 @@ def _instrumented_lambda_handler_call( # noqa pylint: disable=too-many-branches lambda_context.aws_request_id, ) - result = call_wrapped(*args, **kwargs) + exception = None + try: + result = call_wrapped(*args, **kwargs) + except Exception as exc: # pylint: disable=W0703 + exception = exc + span.set_status(Status(StatusCode.ERROR)) # If the request came from an API Gateway, extract http attributes from the event # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/instrumentation/aws-lambda.md#api-gateway @@ -398,6 +404,9 @@ def _instrumented_lambda_handler_call( # noqa pylint: disable=too-many-branches "MeterProvider was missing `force_flush` method. This is necessary in case of a Lambda freeze and would exist in the OTel SDK implementation." ) + if exception is not None: + raise exception.with_traceback(exception.__traceback__) + return result wrap_function_wrapper( From 667e0f629c1504ad5cc5f1631cf6f93a6c28e16a Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Thu, 15 Feb 2024 16:58:15 -0800 Subject: [PATCH 2/6] Add record_exception --- .../src/opentelemetry/instrumentation/aws_lambda/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py index ae04b63041..c1b92d4c8a 100644 --- a/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py @@ -357,6 +357,7 @@ def _instrumented_lambda_handler_call( # noqa pylint: disable=too-many-branches except Exception as exc: # pylint: disable=W0703 exception = exc span.set_status(Status(StatusCode.ERROR)) + span.record_exception(exception) # If the request came from an API Gateway, extract http attributes from the event # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/instrumentation/aws-lambda.md#api-gateway From 33ac0326576ebcc1f20442f3fc60f7e41bc2e75d Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Thu, 15 Feb 2024 17:16:34 -0800 Subject: [PATCH 3/6] pylint disables --- .../src/opentelemetry/instrumentation/aws_lambda/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py index c1b92d4c8a..130622f8c8 100644 --- a/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py @@ -279,6 +279,7 @@ def _set_api_gateway_v2_proxy_attributes( return span +# pylint: disable=too-many-statements def _instrument( wrapped_module_name, wrapped_function_name, @@ -288,6 +289,8 @@ def _instrument( disable_aws_context_propagation: bool = False, meter_provider: MeterProvider = None, ): + # pylint: disable=too-many-locals + # pylint: disable=too-many-statements def _instrumented_lambda_handler_call( # noqa pylint: disable=too-many-branches call_wrapped, instance, args, kwargs ): From d6f3c1b4be4433f47eb8d1bd792d0e8d52ef6275 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Fri, 16 Feb 2024 15:15:39 -0800 Subject: [PATCH 4/6] Add lambda test --- .../tests/mocks/lambda_function.py | 4 ++++ .../test_aws_lambda_instrumentation_manual.py | 23 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/lambda_function.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/lambda_function.py index 259375c481..a878d0f06a 100644 --- a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/lambda_function.py +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/lambda_function.py @@ -19,3 +19,7 @@ def handler(event, context): def rest_api_handler(event, context): return {"statusCode": 200, "body": "200 ok"} + + +def handler_exc(event, context): + raise Exception("500 internal server error") diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py index 5e4aaf0312..070579ef6f 100644 --- a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py @@ -40,7 +40,7 @@ from opentelemetry.semconv.resource import ResourceAttributes from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.test.test_base import TestBase -from opentelemetry.trace import NoOpTracerProvider, SpanKind +from opentelemetry.trace import NoOpTracerProvider, SpanKind, StatusCode from opentelemetry.trace.propagation.tracecontext import ( TraceContextTextMapPropagator, ) @@ -410,6 +410,27 @@ def test_lambda_handles_list_event(self): assert spans + def test_lambda_handles_handler_exception(self): + self.exc_env_patch = mock.patch.dict( + "os.environ", + {_HANDLER: "tests.mocks.lambda_function.handler_exc"}, + ) + self.exc_env_patch.start() + AwsLambdaInstrumentor().instrument() + # instrumentor re-raises the exception + with self.assertRaises(Exception): + mock_execute_lambda() + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 1) + span = spans[0] + self.assertEqual(span.status.status_code, StatusCode.ERROR) + self.assertEqual(len(span.events), 1) + event = span.events[0] + self.assertEqual(event.name, "exception") + + self.exc_env_patch.stop() + def test_uninstrument(self): AwsLambdaInstrumentor().instrument() From f16588700d50c0e741603fa1d21e8abaeec65a41 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Fri, 16 Feb 2024 15:28:08 -0800 Subject: [PATCH 5/6] lint --- .../tests/test_aws_lambda_instrumentation_manual.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py index 070579ef6f..2fa4aafee5 100644 --- a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py @@ -411,11 +411,11 @@ def test_lambda_handles_list_event(self): assert spans def test_lambda_handles_handler_exception(self): - self.exc_env_patch = mock.patch.dict( + exc_env_patch = mock.patch.dict( "os.environ", {_HANDLER: "tests.mocks.lambda_function.handler_exc"}, ) - self.exc_env_patch.start() + exc_env_patch.start() AwsLambdaInstrumentor().instrument() # instrumentor re-raises the exception with self.assertRaises(Exception): @@ -429,7 +429,7 @@ def test_lambda_handles_handler_exception(self): event = span.events[0] self.assertEqual(event.name, "exception") - self.exc_env_patch.stop() + exc_env_patch.stop() def test_uninstrument(self): AwsLambdaInstrumentor().instrument() From 1bba3bf7e3f9ba1535e0feb1aeef1ce1696dd168 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Fri, 16 Feb 2024 16:26:54 -0800 Subject: [PATCH 6/6] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a31f1a2d5..421d28888e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ 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)) ## Version 1.22.0/0.43b0 (2023-12-14)