Skip to content

Commit a359766

Browse files
committed
improv: add tests for capture_response/error env vars
1 parent 8ab3507 commit a359766

File tree

6 files changed

+57
-29
lines changed

6 files changed

+57
-29
lines changed

aws_lambda_powertools/shared/__init__.py

Whitespace-only changes.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import os
2+
3+
TRACER_CAPTURE_RESPONSE_ENV: str = os.getenv("POWERTOOLS_TRACER_CAPTURE_RESPONSE", "true")
4+
TRACER_CAPTURE_ERROR_ENV: str = os.getenv("POWERTOOLS_TRACER_CAPTURE_ERROR", "true")
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from distutils.util import strtobool
2+
3+
4+
def resolve_env_var_choice(env: str, choice: bool = None) -> bool:
5+
return choice if choice is not None else strtobool(env)

aws_lambda_powertools/tracing/tracer.py

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
import logging
66
import os
77
from distutils.util import strtobool
8-
from typing import Any, Callable, Dict, List, Tuple
8+
from typing import Any, Callable, Dict, List, Optional, Tuple
99

1010
import aws_xray_sdk
1111
import aws_xray_sdk.core
1212

13+
from aws_lambda_powertools.shared.constants import TRACER_CAPTURE_ERROR_ENV, TRACER_CAPTURE_RESPONSE_ENV
14+
from aws_lambda_powertools.shared.functions import resolve_env_var_choice
15+
1316
is_cold_start = True
1417
logger = logging.getLogger(__name__)
1518

@@ -233,8 +236,8 @@ def patch(self, modules: Tuple[str] = None):
233236
def capture_lambda_handler(
234237
self,
235238
lambda_handler: Callable[[Dict, Any], Any] = None,
236-
capture_response: bool = True,
237-
capture_error: bool = True,
239+
capture_response: Optional[bool] = None,
240+
capture_error: Optional[bool] = None,
238241
):
239242
"""Decorator to create subsegment for lambda handlers
240243
@@ -246,7 +249,7 @@ def capture_lambda_handler(
246249
lambda_handler : Callable
247250
Method to annotate on
248251
capture_response : bool, optional
249-
Instructs tracer to not include handler's response as metadata, by default True
252+
Instructs tracer to not include handler's response as metadata
250253
capture_error : bool, optional
251254
Instructs tracer to not include handler's error as metadata, by default True
252255
@@ -281,10 +284,8 @@ def handler(event, context):
281284

282285
lambda_handler_name = lambda_handler.__name__
283286

284-
capture_response_env_option = str(os.getenv("POWERTOOLS_TRACER_CAPTURE_RESPONSE", "false"))
285-
capture_error_env_option = str(os.getenv("POWERTOOLS_TRACER_CAPTURE_ERROR", "false"))
286-
capture_response = strtobool(capture_response_env_option) or capture_response
287-
capture_error = strtobool(capture_error_env_option) or capture_error
287+
capture_response = resolve_env_var_choice(env=TRACER_CAPTURE_RESPONSE_ENV, choice=capture_response)
288+
capture_error = resolve_env_var_choice(env=TRACER_CAPTURE_ERROR_ENV, choice=capture_error)
288289

289290
@functools.wraps(lambda_handler)
290291
def decorate(event, context):
@@ -316,7 +317,9 @@ def decorate(event, context):
316317

317318
return decorate
318319

319-
def capture_method(self, method: Callable = None, capture_response: bool = True, capture_error: bool = True):
320+
def capture_method(
321+
self, method: Callable = None, capture_response: Optional[bool] = None, capture_error: Optional[bool] = None
322+
):
320323
"""Decorator to create subsegment for arbitrary functions
321324
322325
It also captures both response and exceptions as metadata
@@ -335,7 +338,7 @@ def capture_method(self, method: Callable = None, capture_response: bool = True,
335338
method : Callable
336339
Method to annotate on
337340
capture_response : bool, optional
338-
Instructs tracer to not include method's response as metadata, by default True
341+
Instructs tracer to not include method's response as metadata
339342
capture_error : bool, optional
340343
Instructs tracer to not include handler's error as metadata, by default True
341344
@@ -475,28 +478,31 @@ async def async_tasks():
475478

476479
method_name = f"{method.__name__}"
477480

481+
capture_response = resolve_env_var_choice(env=TRACER_CAPTURE_RESPONSE_ENV, choice=capture_response)
482+
capture_error = resolve_env_var_choice(env=TRACER_CAPTURE_ERROR_ENV, choice=capture_error)
483+
478484
if inspect.iscoroutinefunction(method):
479485
return self._decorate_async_function(
480-
method=method, capture_response=capture_response, capture_error=True, method_name=method_name
486+
method=method, capture_response=capture_response, capture_error=capture_error, method_name=method_name
481487
)
482488
elif inspect.isgeneratorfunction(method):
483489
return self._decorate_generator_function(
484-
method=method, capture_response=capture_response, capture_error=True, method_name=method_name
490+
method=method, capture_response=capture_response, capture_error=capture_error, method_name=method_name
485491
)
486492
elif hasattr(method, "__wrapped__") and inspect.isgeneratorfunction(method.__wrapped__):
487493
return self._decorate_generator_function_with_context_manager(
488-
method=method, capture_response=capture_response, capture_error=True, method_name=method_name
494+
method=method, capture_response=capture_response, capture_error=capture_error, method_name=method_name
489495
)
490496
else:
491497
return self._decorate_sync_function(
492-
method=method, capture_response=capture_response, capture_error=True, method_name=method_name
498+
method=method, capture_response=capture_response, capture_error=capture_error, method_name=method_name
493499
)
494500

495501
def _decorate_async_function(
496502
self,
497503
method: Callable = None,
498-
capture_response: bool = True,
499-
capture_error: bool = True,
504+
capture_response: Optional[bool] = None,
505+
capture_error: Optional[bool] = None,
500506
method_name: str = None,
501507
):
502508
@functools.wraps(method)
@@ -522,8 +528,8 @@ async def decorate(*args, **kwargs):
522528
def _decorate_generator_function(
523529
self,
524530
method: Callable = None,
525-
capture_response: bool = True,
526-
capture_error: bool = True,
531+
capture_response: Optional[bool] = None,
532+
capture_error: Optional[bool] = None,
527533
method_name: str = None,
528534
):
529535
@functools.wraps(method)
@@ -549,8 +555,8 @@ def decorate(*args, **kwargs):
549555
def _decorate_generator_function_with_context_manager(
550556
self,
551557
method: Callable = None,
552-
capture_response: bool = True,
553-
capture_error: bool = True,
558+
capture_response: Optional[bool] = None,
559+
capture_error: Optional[bool] = None,
554560
method_name: str = None,
555561
):
556562
@functools.wraps(method)
@@ -577,8 +583,8 @@ def decorate(*args, **kwargs):
577583
def _decorate_sync_function(
578584
self,
579585
method: Callable = None,
580-
capture_response: bool = True,
581-
capture_error: bool = True,
586+
capture_response: Optional[bool] = None,
587+
capture_error: Optional[bool] = None,
582588
method_name: str = None,
583589
):
584590
@functools.wraps(method)
@@ -609,7 +615,7 @@ def _add_response_as_metadata(
609615
method_name: str = None,
610616
data: Any = None,
611617
subsegment: aws_xray_sdk.core.models.subsegment = None,
612-
capture_response: bool = True,
618+
capture_response: Optional[bool] = None,
613619
):
614620
"""Add response as metadata for given subsegment
615621
@@ -622,7 +628,7 @@ def _add_response_as_metadata(
622628
subsegment : aws_xray_sdk.core.models.subsegment, optional
623629
existing subsegment to add metadata on, by default None
624630
capture_response : bool, optional
625-
Do not include response as metadata, by default True
631+
Do not include response as metadata
626632
"""
627633
if data is None or not capture_response or subsegment is None:
628634
return
@@ -634,7 +640,7 @@ def _add_full_exception_as_metadata(
634640
method_name: str = None,
635641
error: Exception = None,
636642
subsegment: aws_xray_sdk.core.models.subsegment = None,
637-
capture_error: bool = True,
643+
capture_error: Optional[bool] = None,
638644
):
639645
"""Add full exception object as metadata for given subsegment
640646
@@ -649,7 +655,7 @@ def _add_full_exception_as_metadata(
649655
capture_error : bool, optional
650656
Do not include error as metadata, by default True
651657
"""
652-
if error is None or not capture_error or subsegment is None:
658+
if not capture_error:
653659
return
654660

655661
subsegment.put_metadata(key=f"{method_name} error", value=error, namespace=self._config["service"])
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import os
2+
3+
from aws_lambda_powertools.shared.functions import resolve_env_var_choice
4+
5+
6+
def test_explicit_wins_over_env_var():
7+
choice_env = os.getenv("CHOICE", True)
8+
9+
choice = resolve_env_var_choice(env=choice_env, choice=False)
10+
11+
assert choice is False

tests/unit/test_tracing.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -502,13 +502,15 @@ def generator_fn():
502502
assert str(put_metadata_mock_args["value"]) == "test"
503503

504504

505-
def test_tracer_lambda_handler_does_not_add_response_as_metadata(mocker, provider_stub, in_subsegment_mock):
505+
def test_tracer_lambda_handler_override_response_as_metadata(mocker, provider_stub, in_subsegment_mock):
506506
# GIVEN tracer is initialized
507507
provider = provider_stub(in_subsegment=in_subsegment_mock.in_subsegment)
508+
509+
mocker.patch("aws_lambda_powertools.tracing.tracer.TRACER_CAPTURE_RESPONSE_ENV", return_value=True)
508510
tracer = Tracer(provider=provider, auto_patch=False)
509511

510512
# WHEN capture_lambda_handler decorator is used
511-
# and the handler response is empty
513+
# with capture_response set to False
512514
@tracer.capture_lambda_handler(capture_response=False)
513515
def handler(event, context):
514516
return "response"
@@ -519,7 +521,7 @@ def handler(event, context):
519521
assert in_subsegment_mock.put_metadata.call_count == 0
520522

521523

522-
def test_tracer_method_does_not_add_response_as_metadata(mocker, provider_stub, in_subsegment_mock):
524+
def test_tracer_method_override_response_as_metadata(provider_stub, in_subsegment_mock):
523525
# GIVEN tracer is initialized
524526
provider = provider_stub(in_subsegment=in_subsegment_mock.in_subsegment)
525527
tracer = Tracer(provider=provider, auto_patch=False)

0 commit comments

Comments
 (0)