Skip to content

Commit efb327d

Browse files
AwsLambdaInstrumentor handles and re-raises handler function exception (#2245)
1 parent 2518a4a commit efb327d

File tree

4 files changed

+41
-2
lines changed

4 files changed

+41
-2
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1919
([#2136](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2136))
2020
- `opentelemetry-resource-detector-azure` Suppress instrumentation for `urllib` call
2121
([#2178](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2178))
22+
- AwsLambdaInstrumentor handles and re-raises function exception ([#2245](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2245))
2223

2324
## Version 1.22.0/0.43b0 (2023-12-14)
2425

instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ def custom_event_context_extractor(lambda_event):
9797
get_tracer_provider,
9898
)
9999
from opentelemetry.trace.propagation import get_current_span
100+
from opentelemetry.trace.status import Status, StatusCode
100101

101102
logger = logging.getLogger(__name__)
102103

@@ -278,6 +279,7 @@ def _set_api_gateway_v2_proxy_attributes(
278279
return span
279280

280281

282+
# pylint: disable=too-many-statements
281283
def _instrument(
282284
wrapped_module_name,
283285
wrapped_function_name,
@@ -287,6 +289,8 @@ def _instrument(
287289
disable_aws_context_propagation: bool = False,
288290
meter_provider: MeterProvider = None,
289291
):
292+
# pylint: disable=too-many-locals
293+
# pylint: disable=too-many-statements
290294
def _instrumented_lambda_handler_call( # noqa pylint: disable=too-many-branches
291295
call_wrapped, instance, args, kwargs
292296
):
@@ -350,7 +354,13 @@ def _instrumented_lambda_handler_call( # noqa pylint: disable=too-many-branches
350354
lambda_context.aws_request_id,
351355
)
352356

353-
result = call_wrapped(*args, **kwargs)
357+
exception = None
358+
try:
359+
result = call_wrapped(*args, **kwargs)
360+
except Exception as exc: # pylint: disable=W0703
361+
exception = exc
362+
span.set_status(Status(StatusCode.ERROR))
363+
span.record_exception(exception)
354364

355365
# If the request came from an API Gateway, extract http attributes from the event
356366
# https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/instrumentation/aws-lambda.md#api-gateway
@@ -398,6 +408,9 @@ def _instrumented_lambda_handler_call( # noqa pylint: disable=too-many-branches
398408
"MeterProvider was missing `force_flush` method. This is necessary in case of a Lambda freeze and would exist in the OTel SDK implementation."
399409
)
400410

411+
if exception is not None:
412+
raise exception.with_traceback(exception.__traceback__)
413+
401414
return result
402415

403416
wrap_function_wrapper(

instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/lambda_function.py

+4
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,7 @@ def handler(event, context):
1919

2020
def rest_api_handler(event, context):
2121
return {"statusCode": 200, "body": "200 ok"}
22+
23+
24+
def handler_exc(event, context):
25+
raise Exception("500 internal server error")

instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py

+22-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
from opentelemetry.semconv.resource import ResourceAttributes
4141
from opentelemetry.semconv.trace import SpanAttributes
4242
from opentelemetry.test.test_base import TestBase
43-
from opentelemetry.trace import NoOpTracerProvider, SpanKind
43+
from opentelemetry.trace import NoOpTracerProvider, SpanKind, StatusCode
4444
from opentelemetry.trace.propagation.tracecontext import (
4545
TraceContextTextMapPropagator,
4646
)
@@ -410,6 +410,27 @@ def test_lambda_handles_list_event(self):
410410

411411
assert spans
412412

413+
def test_lambda_handles_handler_exception(self):
414+
exc_env_patch = mock.patch.dict(
415+
"os.environ",
416+
{_HANDLER: "tests.mocks.lambda_function.handler_exc"},
417+
)
418+
exc_env_patch.start()
419+
AwsLambdaInstrumentor().instrument()
420+
# instrumentor re-raises the exception
421+
with self.assertRaises(Exception):
422+
mock_execute_lambda()
423+
424+
spans = self.memory_exporter.get_finished_spans()
425+
self.assertEqual(len(spans), 1)
426+
span = spans[0]
427+
self.assertEqual(span.status.status_code, StatusCode.ERROR)
428+
self.assertEqual(len(span.events), 1)
429+
event = span.events[0]
430+
self.assertEqual(event.name, "exception")
431+
432+
exc_env_patch.stop()
433+
413434
def test_uninstrument(self):
414435
AwsLambdaInstrumentor().instrument()
415436

0 commit comments

Comments
 (0)