From 4e7236d0c8726cb05b300c9d22e1eb58889ca936 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Sat, 22 Aug 2020 20:04:26 +0200 Subject: [PATCH 1/9] feat: capture_response as metadata option #127 --- aws_lambda_powertools/tracing/tracer.py | 99 ++++++++++++++++++------- tests/unit/test_tracing.py | 34 +++++++++ 2 files changed, 107 insertions(+), 26 deletions(-) diff --git a/aws_lambda_powertools/tracing/tracer.py b/aws_lambda_powertools/tracing/tracer.py index 4c12be3fc26..bac70ddaeb5 100644 --- a/aws_lambda_powertools/tracing/tracer.py +++ b/aws_lambda_powertools/tracing/tracer.py @@ -226,12 +226,19 @@ def patch(self, modules: Tuple[str] = None): else: aws_xray_sdk.core.patch(modules) - def capture_lambda_handler(self, lambda_handler: Callable[[Dict, Any], Any] = None): + def capture_lambda_handler(self, lambda_handler: Callable[[Dict, Any], Any] = None, capture_response: bool = True): """Decorator to create subsegment for lambda handlers As Lambda follows (event, context) signature we can remove some of the boilerplate and also capture any exception any Lambda function throws or its response as metadata + Parameters + ---------- + lambda_handler : Callable + Method to annotate on + capture_response : bool, optional + Instructs tracer to not include handler's response as metadata, by default True + Example ------- **Lambda function using capture_lambda_handler decorator** @@ -241,16 +248,24 @@ def capture_lambda_handler(self, lambda_handler: Callable[[Dict, Any], Any] = No def handler(event, context): ... - Parameters - ---------- - method : Callable - Method to annotate on + **Preventing Tracer to log response as metadata** + + tracer = Tracer(service="payment") + @tracer.capture_lambda_handler(capture_response=False) + def handler(event, context): + ... Raises ------ err Exception raised by method """ + # If handler is None we've been called with parameters + # Return a partial function with args filled + if lambda_handler is None: + logger.debug("Decorator called with parameters") + return functools.partial(self.capture_lambda_handler, capture_response=capture_response) + lambda_handler_name = lambda_handler.__name__ @functools.wraps(lambda_handler) @@ -268,7 +283,10 @@ def decorate(event, context): logger.debug("Received lambda handler response successfully") logger.debug(response) self._add_response_as_metadata( - function_name=lambda_handler_name, data=response, subsegment=subsegment + function_name=lambda_handler_name, + data=response, + subsegment=subsegment, + capture_response=capture_response, ) except Exception as err: logger.exception(f"Exception received from {lambda_handler_name}") @@ -281,7 +299,7 @@ def decorate(event, context): return decorate - def capture_method(self, method: Callable = None): + def capture_method(self, method: Callable = None, capture_response: bool = True): """Decorator to create subsegment for arbitrary functions It also captures both response and exceptions as metadata @@ -295,6 +313,13 @@ def capture_method(self, method: Callable = None): `async.gather` is called, or use `in_subsegment_async` context manager via our escape hatch mechanism - See examples. + Parameters + ---------- + method : Callable + Method to annotate on + capture_response : bool, optional + Instructs tracer to not include method's response as metadata, by default True + Example ------- **Custom function using capture_method decorator** @@ -416,29 +441,31 @@ async def async_tasks(): return { "task": "done", **ret } - Parameters - ---------- - method : Callable - Method to annotate on - Raises ------ err Exception raised by method """ + # If method is None we've been called with parameters + # Return a partial function with args filled + if method is None: + logger.debug("Decorator called with parameters") + return functools.partial(self.capture_method, capture_response=capture_response) if inspect.iscoroutinefunction(method): - decorate = self._decorate_async_function(method=method) + decorate = self._decorate_async_function(method=method, capture_response=capture_response) elif inspect.isgeneratorfunction(method): - decorate = self._decorate_generator_function(method=method) + decorate = self._decorate_generator_function(method=method, capture_response=capture_response) elif hasattr(method, "__wrapped__") and inspect.isgeneratorfunction(method.__wrapped__): - decorate = self._decorate_generator_function_with_context_manager(method=method) + decorate = self._decorate_generator_function_with_context_manager( + method=method, capture_response=capture_response + ) else: - decorate = self._decorate_sync_function(method=method) + decorate = self._decorate_sync_function(method=method, capture_response=capture_response) return decorate - def _decorate_async_function(self, method: Callable = None): + def _decorate_async_function(self, method: Callable = None, capture_response: bool = True): method_name = f"{method.__name__}" @functools.wraps(method) @@ -447,7 +474,12 @@ async def decorate(*args, **kwargs): try: logger.debug(f"Calling method: {method_name}") response = await method(*args, **kwargs) - self._add_response_as_metadata(function_name=method_name, data=response, subsegment=subsegment) + self._add_response_as_metadata( + function_name=method_name, + data=response, + subsegment=subsegment, + capture_response=capture_response, + ) except Exception as err: logger.exception(f"Exception received from '{method_name}' method") self._add_full_exception_as_metadata(function_name=method_name, error=err, subsegment=subsegment) @@ -457,7 +489,7 @@ async def decorate(*args, **kwargs): return decorate - def _decorate_generator_function(self, method: Callable = None): + def _decorate_generator_function(self, method: Callable = None, capture_response: bool = True): method_name = f"{method.__name__}" @functools.wraps(method) @@ -466,7 +498,9 @@ def decorate(*args, **kwargs): try: logger.debug(f"Calling method: {method_name}") result = yield from method(*args, **kwargs) - self._add_response_as_metadata(function_name=method_name, data=result, subsegment=subsegment) + self._add_response_as_metadata( + function_name=method_name, data=result, subsegment=subsegment, capture_response=capture_response + ) except Exception as err: logger.exception(f"Exception received from '{method_name}' method") self._add_full_exception_as_metadata(function_name=method_name, error=err, subsegment=subsegment) @@ -476,7 +510,7 @@ def decorate(*args, **kwargs): return decorate - def _decorate_generator_function_with_context_manager(self, method: Callable = None): + def _decorate_generator_function_with_context_manager(self, method: Callable = None, capture_response: bool = True): method_name = f"{method.__name__}" @functools.wraps(method) @@ -488,7 +522,9 @@ def decorate(*args, **kwargs): with method(*args, **kwargs) as return_val: result = return_val yield result - self._add_response_as_metadata(function_name=method_name, data=result, subsegment=subsegment) + self._add_response_as_metadata( + function_name=method_name, data=result, subsegment=subsegment, capture_response=capture_response + ) except Exception as err: logger.exception(f"Exception received from '{method_name}' method") self._add_full_exception_as_metadata(function_name=method_name, error=err, subsegment=subsegment) @@ -496,7 +532,7 @@ def decorate(*args, **kwargs): return decorate - def _decorate_sync_function(self, method: Callable = None): + def _decorate_sync_function(self, method: Callable = None, capture_response: bool = True): method_name = f"{method.__name__}" @functools.wraps(method) @@ -505,7 +541,12 @@ def decorate(*args, **kwargs): try: logger.debug(f"Calling method: {method_name}") response = method(*args, **kwargs) - self._add_response_as_metadata(function_name=method_name, data=response, subsegment=subsegment) + self._add_response_as_metadata( + function_name=method_name, + data=response, + subsegment=subsegment, + capture_response=capture_response, + ) except Exception as err: logger.exception(f"Exception received from '{method_name}' method") self._add_full_exception_as_metadata(function_name=method_name, error=err, subsegment=subsegment) @@ -516,7 +557,11 @@ def decorate(*args, **kwargs): return decorate def _add_response_as_metadata( - self, function_name: str = None, data: Any = None, subsegment: aws_xray_sdk.core.models.subsegment = None + self, + function_name: str = None, + data: Any = None, + subsegment: aws_xray_sdk.core.models.subsegment = None, + capture_response: bool = True, ): """Add response as metadata for given subsegment @@ -528,8 +573,10 @@ def _add_response_as_metadata( data to add as subsegment metadata, by default None subsegment : aws_xray_sdk.core.models.subsegment, optional existing subsegment to add metadata on, by default None + capture_response : bool, optional + Do not include response as metadata, by default True """ - if data is None or subsegment is None: + if data is None or not capture_response or subsegment is None: return subsegment.put_metadata(key=f"{function_name} response", value=data, namespace=self._config["service"]) diff --git a/tests/unit/test_tracing.py b/tests/unit/test_tracing.py index 16c476ee0fc..f948b046313 100644 --- a/tests/unit/test_tracing.py +++ b/tests/unit/test_tracing.py @@ -502,3 +502,37 @@ def generator_fn(): assert put_metadata_mock_args["namespace"] == "booking" assert isinstance(put_metadata_mock_args["value"], ValueError) assert str(put_metadata_mock_args["value"]) == "test" + + +def test_tracer_lambda_handler_does_not_add_response_as_metadata(mocker, provider_stub, in_subsegment_mock): + # GIVEN tracer is initialized + provider = provider_stub(in_subsegment=in_subsegment_mock.in_subsegment) + tracer = Tracer(provider=provider, auto_patch=False) + + # WHEN capture_lambda_handler decorator is used + # and the handler response is empty + @tracer.capture_lambda_handler(capture_response=False) + def handler(event, context): + return "response" + + handler({}, mocker.MagicMock()) + + # THEN we should not add any metadata + assert in_subsegment_mock.put_metadata.call_count == 0 + + +def test_tracer_method_does_not_add_response_as_metadata(mocker, provider_stub, in_subsegment_mock): + # GIVEN tracer is initialized + provider = provider_stub(in_subsegment=in_subsegment_mock.in_subsegment) + tracer = Tracer(provider=provider, auto_patch=False) + + # WHEN capture_method decorator is used + # and the method response is empty + @tracer.capture_method(capture_response=False) + def greeting(name, message): + return "response" + + greeting(name="Foo", message="Bar") + + # THEN we should not add any metadata + assert in_subsegment_mock.put_metadata.call_count == 0 From 3ff8a013fd174b7d2c71489a0caffd325abd583a Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Sat, 22 Aug 2020 20:08:45 +0200 Subject: [PATCH 2/9] fix: correct in_subsegment assertion --- tests/unit/test_tracing.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/unit/test_tracing.py b/tests/unit/test_tracing.py index f948b046313..3e2492b9e15 100644 --- a/tests/unit/test_tracing.py +++ b/tests/unit/test_tracing.py @@ -179,10 +179,9 @@ def test_tracer_no_autopatch(patch_mock): assert patch_mock.call_count == 0 -def test_tracer_lambda_handler_does_not_add_empty_response_as_metadata(mocker, provider_stub): +def test_tracer_lambda_handler_does_not_add_empty_response_as_metadata(mocker, provider_stub, in_subsegment_mock): # GIVEN tracer is initialized - put_metadata_mock = mocker.MagicMock() - provider = provider_stub(put_metadata_mock=put_metadata_mock) + provider = provider_stub(in_subsegment=in_subsegment_mock.in_subsegment) tracer = Tracer(provider=provider) # WHEN capture_lambda_handler decorator is used @@ -194,13 +193,12 @@ def handler(event, context): handler({}, mocker.MagicMock()) # THEN we should not add empty metadata - assert put_metadata_mock.call_count == 0 + assert in_subsegment_mock.put_metadata.call_count == 0 -def test_tracer_method_does_not_add_empty_response_as_metadata(mocker, provider_stub): +def test_tracer_method_does_not_add_empty_response_as_metadata(mocker, provider_stub, in_subsegment_mock): # GIVEN tracer is initialized - put_metadata_mock = mocker.MagicMock() - provider = provider_stub(put_metadata_mock=put_metadata_mock) + provider = provider_stub(in_subsegment=in_subsegment_mock.in_subsegment) tracer = Tracer(provider=provider) # WHEN capture_method decorator is used @@ -212,7 +210,7 @@ def greeting(name, message): greeting(name="Foo", message="Bar") # THEN we should not add empty metadata - assert put_metadata_mock.call_count == 0 + assert in_subsegment_mock.put_metadata.call_count == 0 @mock.patch("aws_lambda_powertools.tracing.tracer.aws_xray_sdk.core.patch") From 582f023db81880c9dc04ee8665f2b8f782469ffc Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Sat, 22 Aug 2020 20:19:20 +0200 Subject: [PATCH 3/9] improv: naming consistency --- aws_lambda_powertools/tracing/tracer.py | 82 +++++++++++++------------ 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/aws_lambda_powertools/tracing/tracer.py b/aws_lambda_powertools/tracing/tracer.py index bac70ddaeb5..34dbbf5faf9 100644 --- a/aws_lambda_powertools/tracing/tracer.py +++ b/aws_lambda_powertools/tracing/tracer.py @@ -158,19 +158,19 @@ def __init__( def put_annotation(self, key: str, value: Any): """Adds annotation to existing segment or subsegment + Parameters + ---------- + key : str + Annotation key + value : any + Value for annotation + Example ------- Custom annotation for a pseudo service named payment tracer = Tracer(service="payment") tracer.put_annotation("PaymentStatus", "CONFIRMED") - - Parameters - ---------- - key : str - Annotation key (e.g. PaymentStatus) - value : any - Value for annotation (e.g. "CONFIRMED") """ if self.disabled: logger.debug("Tracing has been disabled, aborting put_annotation") @@ -283,7 +283,7 @@ def decorate(event, context): logger.debug("Received lambda handler response successfully") logger.debug(response) self._add_response_as_metadata( - function_name=lambda_handler_name, + method_name=lambda_handler_name, data=response, subsegment=subsegment, capture_response=capture_response, @@ -291,7 +291,7 @@ def decorate(event, context): except Exception as err: logger.exception(f"Exception received from {lambda_handler_name}") self._add_full_exception_as_metadata( - function_name=lambda_handler_name, error=err, subsegment=subsegment + method_name=lambda_handler_name, error=err, subsegment=subsegment ) raise @@ -452,22 +452,28 @@ async def async_tasks(): logger.debug("Decorator called with parameters") return functools.partial(self.capture_method, capture_response=capture_response) + method_name = f"{method.__name__}" + if inspect.iscoroutinefunction(method): - decorate = self._decorate_async_function(method=method, capture_response=capture_response) + decorate = self._decorate_async_function( + method=method, capture_response=capture_response, method_name=method_name + ) elif inspect.isgeneratorfunction(method): - decorate = self._decorate_generator_function(method=method, capture_response=capture_response) + decorate = self._decorate_generator_function( + method=method, capture_response=capture_response, method_name=method_name + ) elif hasattr(method, "__wrapped__") and inspect.isgeneratorfunction(method.__wrapped__): decorate = self._decorate_generator_function_with_context_manager( - method=method, capture_response=capture_response + method=method, capture_response=capture_response, method_name=method_name ) else: - decorate = self._decorate_sync_function(method=method, capture_response=capture_response) + decorate = self._decorate_sync_function( + method=method, capture_response=capture_response, method_name=method_name + ) return decorate - def _decorate_async_function(self, method: Callable = None, capture_response: bool = True): - method_name = f"{method.__name__}" - + def _decorate_async_function(self, method: Callable = None, capture_response: bool = True, method_name: str = None): @functools.wraps(method) async def decorate(*args, **kwargs): async with self.provider.in_subsegment_async(name=f"## {method_name}") as subsegment: @@ -475,23 +481,23 @@ async def decorate(*args, **kwargs): logger.debug(f"Calling method: {method_name}") response = await method(*args, **kwargs) self._add_response_as_metadata( - function_name=method_name, + method_name=method_name, data=response, subsegment=subsegment, capture_response=capture_response, ) except Exception as err: logger.exception(f"Exception received from '{method_name}' method") - self._add_full_exception_as_metadata(function_name=method_name, error=err, subsegment=subsegment) + self._add_full_exception_as_metadata(method_name=method_name, error=err, subsegment=subsegment) raise return response return decorate - def _decorate_generator_function(self, method: Callable = None, capture_response: bool = True): - method_name = f"{method.__name__}" - + def _decorate_generator_function( + self, method: Callable = None, capture_response: bool = True, method_name: str = None + ): @functools.wraps(method) def decorate(*args, **kwargs): with self.provider.in_subsegment(name=f"## {method_name}") as subsegment: @@ -499,20 +505,20 @@ def decorate(*args, **kwargs): logger.debug(f"Calling method: {method_name}") result = yield from method(*args, **kwargs) self._add_response_as_metadata( - function_name=method_name, data=result, subsegment=subsegment, capture_response=capture_response + method_name=method_name, data=result, subsegment=subsegment, capture_response=capture_response ) except Exception as err: logger.exception(f"Exception received from '{method_name}' method") - self._add_full_exception_as_metadata(function_name=method_name, error=err, subsegment=subsegment) + self._add_full_exception_as_metadata(method_name=method_name, error=err, subsegment=subsegment) raise return result return decorate - def _decorate_generator_function_with_context_manager(self, method: Callable = None, capture_response: bool = True): - method_name = f"{method.__name__}" - + def _decorate_generator_function_with_context_manager( + self, method: Callable = None, capture_response: bool = True, method_name: str = None + ): @functools.wraps(method) @contextlib.contextmanager def decorate(*args, **kwargs): @@ -523,18 +529,16 @@ def decorate(*args, **kwargs): result = return_val yield result self._add_response_as_metadata( - function_name=method_name, data=result, subsegment=subsegment, capture_response=capture_response + method_name=method_name, data=result, subsegment=subsegment, capture_response=capture_response ) except Exception as err: logger.exception(f"Exception received from '{method_name}' method") - self._add_full_exception_as_metadata(function_name=method_name, error=err, subsegment=subsegment) + self._add_full_exception_as_metadata(method_name=method_name, error=err, subsegment=subsegment) raise return decorate - def _decorate_sync_function(self, method: Callable = None, capture_response: bool = True): - method_name = f"{method.__name__}" - + def _decorate_sync_function(self, method: Callable = None, capture_response: bool = True, method_name: str = None): @functools.wraps(method) def decorate(*args, **kwargs): with self.provider.in_subsegment(name=f"## {method_name}") as subsegment: @@ -542,14 +546,14 @@ def decorate(*args, **kwargs): logger.debug(f"Calling method: {method_name}") response = method(*args, **kwargs) self._add_response_as_metadata( - function_name=method_name, + method_name=method_name, data=response, subsegment=subsegment, capture_response=capture_response, ) except Exception as err: logger.exception(f"Exception received from '{method_name}' method") - self._add_full_exception_as_metadata(function_name=method_name, error=err, subsegment=subsegment) + self._add_full_exception_as_metadata(method_name=method_name, error=err, subsegment=subsegment) raise return response @@ -558,7 +562,7 @@ def decorate(*args, **kwargs): def _add_response_as_metadata( self, - function_name: str = None, + method_name: str = None, data: Any = None, subsegment: aws_xray_sdk.core.models.subsegment = None, capture_response: bool = True, @@ -567,7 +571,7 @@ def _add_response_as_metadata( Parameters ---------- - function_name : str, optional + method_name : str, optional function name to add as metadata key, by default None data : Any, optional data to add as subsegment metadata, by default None @@ -579,23 +583,23 @@ def _add_response_as_metadata( if data is None or not capture_response or subsegment is None: return - subsegment.put_metadata(key=f"{function_name} response", value=data, namespace=self._config["service"]) + subsegment.put_metadata(key=f"{method_name} response", value=data, namespace=self._config["service"]) def _add_full_exception_as_metadata( - self, function_name: str = None, error: Exception = None, subsegment: aws_xray_sdk.core.models.subsegment = None + self, method_name: str = None, error: Exception = None, subsegment: aws_xray_sdk.core.models.subsegment = None ): """Add full exception object as metadata for given subsegment Parameters ---------- - function_name : str, optional + method_name : str, optional function name to add as metadata key, by default None error : Exception, optional error to add as subsegment metadata, by default None subsegment : aws_xray_sdk.core.models.subsegment, optional existing subsegment to add metadata on, by default None """ - subsegment.put_metadata(key=f"{function_name} error", value=error, namespace=self._config["service"]) + subsegment.put_metadata(key=f"{method_name} error", value=error, namespace=self._config["service"]) def __disable_tracing_provider(self): """Forcefully disables tracing""" From 0a616871a9fd72e09b55203bd0288fdd1c0bbf35 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Sat, 22 Aug 2020 20:29:00 +0200 Subject: [PATCH 4/9] fix: naming and staticmethod consistency --- aws_lambda_powertools/tracing/tracer.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/aws_lambda_powertools/tracing/tracer.py b/aws_lambda_powertools/tracing/tracer.py index 34dbbf5faf9..b21eb80266f 100644 --- a/aws_lambda_powertools/tracing/tracer.py +++ b/aws_lambda_powertools/tracing/tracer.py @@ -150,7 +150,7 @@ def __init__( self.auto_patch = self._config["auto_patch"] if self.disabled: - self.__disable_tracing_provider() + self._disable_tracer_provider() if self.auto_patch: self.patch(modules=patch_modules) @@ -601,12 +601,14 @@ def _add_full_exception_as_metadata( """ subsegment.put_metadata(key=f"{method_name} error", value=error, namespace=self._config["service"]) - def __disable_tracing_provider(self): + @staticmethod + def _disable_tracer_provider(): """Forcefully disables tracing""" logger.debug("Disabling tracer provider...") aws_xray_sdk.global_sdk_config.set_sdk_enabled(False) - def __is_trace_disabled(self) -> bool: + @staticmethod + def _is_tracer_disabled() -> bool: """Detects whether trace has been disabled Tracing is automatically disabled in the following conditions: @@ -643,7 +645,7 @@ def __build_config( provider: aws_xray_sdk.core.xray_recorder = None, ): """ Populates Tracer config for new and existing initializations """ - is_disabled = disabled if disabled is not None else self.__is_trace_disabled() + is_disabled = disabled if disabled is not None else self._is_tracer_disabled() is_service = service if service is not None else os.getenv("POWERTOOLS_SERVICE_NAME") self._config["provider"] = provider if provider is not None else self._config["provider"] From adfee499af4f4f1eacf22c6ad352622a5bceef8d Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Sat, 22 Aug 2020 21:02:24 +0200 Subject: [PATCH 5/9] chore: update changelog to reflect new feature --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index adec16c289a..e27158b968a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - This is a breaking change if you were graphing/alerting on both application metrics with the same name to compensate this previous malfunctioning - Marked as bugfix as this is the intended behaviour since the beginning, as you shouldn't have the same application metric with different dimensions +### Added +- **Tracer**: capture_lambda_handler and capture_method decorators now support `capture_response` parameter to not include function's response as part of tracing metadata + ## [1.3.1] - 2020-08-22 ### Fixed - **Tracer**: capture_method decorator did not properly handle nested context managers From a2f5076ab728b73ff52d0c894eb32d39570efd07 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Sat, 22 Aug 2020 21:18:21 +0200 Subject: [PATCH 6/9] docs: bring new feature upfront when returning sensitive info --- docs/content/core/tracer.mdx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/content/core/tracer.mdx b/docs/content/core/tracer.mdx index 677ad4ccae0..004e1ad6acf 100644 --- a/docs/content/core/tracer.mdx +++ b/docs/content/core/tracer.mdx @@ -17,6 +17,12 @@ Tracer is an opinionated thin wrapper for [AWS X-Ray Python SDK](https://github. * Support tracing async methods, generators, and context managers * Auto patch supported modules, or a tuple of explicit modules supported by AWS X-Ray + + Returning sensitive information from your Lambda handler or functions, where Tracer is used? +

+ You can disable capturing their responses as tracing metadata with capture_response=False parameter for both capture_lambda_handler and capture_method decorators. +

+ ## Initialization Your AWS Lambda function must have permission to send traces to AWS X-Ray - Here is an example using AWS Serverless Application Model (SAM) @@ -63,6 +69,10 @@ def handler(event, context): charge_id = event.get('charge_id') payment = collect_payment(charge_id) ... + +@tracer.capture_lambda_handler(capture_response=False) # highlight-line +def handler(event, context): + return "sensitive_information" ``` ### Annotations @@ -108,7 +118,10 @@ def collect_payment(charge_id): ret = requests.post(PAYMENT_ENDPOINT) # logic tracer.put_annotation("PAYMENT_STATUS", "SUCCESS") # custom annotation return ret -... + +@tracer.capture_method(capture_response=False) # highlight-line +def sensitive_information_to_be_processed(): + return "sensitive_information" ``` ## Asynchronous and generator functions From f182b655b5f477d3e620fc95ff645ab22723b92c Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Sat, 22 Aug 2020 21:20:20 +0200 Subject: [PATCH 7/9] docs: grammar --- docs/content/core/tracer.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/core/tracer.mdx b/docs/content/core/tracer.mdx index 004e1ad6acf..36a0cded9f0 100644 --- a/docs/content/core/tracer.mdx +++ b/docs/content/core/tracer.mdx @@ -20,7 +20,7 @@ Tracer is an opinionated thin wrapper for [AWS X-Ray Python SDK](https://github. Returning sensitive information from your Lambda handler or functions, where Tracer is used?

- You can disable capturing their responses as tracing metadata with capture_response=False parameter for both capture_lambda_handler and capture_method decorators. + You can disable Tracer from capturing their responses as tracing metadata with capture_response=False parameter in both capture_lambda_handler and capture_method decorators.

## Initialization From a68c967c0107923176b5f93a028896e987a4d3db Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Sun, 23 Aug 2020 11:58:23 +0200 Subject: [PATCH 8/9] fix: remove actual response from debug logs --- aws_lambda_powertools/tracing/tracer.py | 1 - 1 file changed, 1 deletion(-) diff --git a/aws_lambda_powertools/tracing/tracer.py b/aws_lambda_powertools/tracing/tracer.py index b21eb80266f..78917fe72ef 100644 --- a/aws_lambda_powertools/tracing/tracer.py +++ b/aws_lambda_powertools/tracing/tracer.py @@ -281,7 +281,6 @@ def decorate(event, context): logger.debug("Calling lambda handler") response = lambda_handler(event, context) logger.debug("Received lambda handler response successfully") - logger.debug(response) self._add_response_as_metadata( method_name=lambda_handler_name, data=response, From 91c05e675e2476157c818f3f177619059633f5ff Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Sun, 23 Aug 2020 12:01:04 +0200 Subject: [PATCH 9/9] chore: update internal docstrings for consistency --- aws_lambda_powertools/tracing/tracer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws_lambda_powertools/tracing/tracer.py b/aws_lambda_powertools/tracing/tracer.py index 78917fe72ef..25caacb651e 100644 --- a/aws_lambda_powertools/tracing/tracer.py +++ b/aws_lambda_powertools/tracing/tracer.py @@ -571,7 +571,7 @@ def _add_response_as_metadata( Parameters ---------- method_name : str, optional - function name to add as metadata key, by default None + method name to add as metadata key, by default None data : Any, optional data to add as subsegment metadata, by default None subsegment : aws_xray_sdk.core.models.subsegment, optional @@ -592,7 +592,7 @@ def _add_full_exception_as_metadata( Parameters ---------- method_name : str, optional - function name to add as metadata key, by default None + method name to add as metadata key, by default None error : Exception, optional error to add as subsegment metadata, by default None subsegment : aws_xray_sdk.core.models.subsegment, optional