55import logging
66import os
77from distutils .util import strtobool
8- from typing import Any , Callable , Dict , List , Tuple
8+ from typing import Any , Callable , Dict , List , Optional , Tuple
99
1010import aws_xray_sdk
1111import 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+
1316is_cold_start = True
1417logger = logging .getLogger (__name__ )
1518
@@ -34,6 +37,10 @@ class Tracer:
3437 disable tracer (e.g. `"true", "True", "TRUE"`)
3538 POWERTOOLS_SERVICE_NAME : str
3639 service name
40+ POWERTOOLS_TRACER_CAPTURE_RESPONSE : str
41+ disable auto-capture response as metadata (e.g. `"true", "True", "TRUE"`)
42+ POWERTOOLS_TRACER_CAPTURE_ERROR : str
43+ disable auto-capture error as metadata (e.g. `"true", "True", "TRUE"`)
3744
3845 Parameters
3946 ----------
@@ -226,7 +233,12 @@ def patch(self, modules: Tuple[str] = None):
226233 else :
227234 aws_xray_sdk .core .patch (modules )
228235
229- def capture_lambda_handler (self , lambda_handler : Callable [[Dict , Any ], Any ] = None , capture_response : bool = True ):
236+ def capture_lambda_handler (
237+ self ,
238+ lambda_handler : Callable [[Dict , Any ], Any ] = None ,
239+ capture_response : Optional [bool ] = None ,
240+ capture_error : Optional [bool ] = None ,
241+ ):
230242 """Decorator to create subsegment for lambda handlers
231243
232244 As Lambda follows (event, context) signature we can remove some of the boilerplate
@@ -237,7 +249,9 @@ def capture_lambda_handler(self, lambda_handler: Callable[[Dict, Any], Any] = No
237249 lambda_handler : Callable
238250 Method to annotate on
239251 capture_response : bool, optional
240- Instructs tracer to not include handler's response as metadata, by default True
252+ Instructs tracer to not include handler's response as metadata
253+ capture_error : bool, optional
254+ Instructs tracer to not include handler's error as metadata, by default True
241255
242256 Example
243257 -------
@@ -264,10 +278,15 @@ def handler(event, context):
264278 # Return a partial function with args filled
265279 if lambda_handler is None :
266280 logger .debug ("Decorator called with parameters" )
267- return functools .partial (self .capture_lambda_handler , capture_response = capture_response )
281+ return functools .partial (
282+ self .capture_lambda_handler , capture_response = capture_response , capture_error = capture_error
283+ )
268284
269285 lambda_handler_name = lambda_handler .__name__
270286
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 )
289+
271290 @functools .wraps (lambda_handler )
272291 def decorate (event , context ):
273292 with self .provider .in_subsegment (name = f"## { lambda_handler_name } " ) as subsegment :
@@ -290,15 +309,17 @@ def decorate(event, context):
290309 except Exception as err :
291310 logger .exception (f"Exception received from { lambda_handler_name } " )
292311 self ._add_full_exception_as_metadata (
293- method_name = lambda_handler_name , error = err , subsegment = subsegment
312+ method_name = lambda_handler_name , error = err , subsegment = subsegment , capture_error = capture_error
294313 )
295314 raise
296315
297316 return response
298317
299318 return decorate
300319
301- def capture_method (self , method : Callable = None , capture_response : bool = True ):
320+ def capture_method (
321+ self , method : Callable = None , capture_response : Optional [bool ] = None , capture_error : Optional [bool ] = None
322+ ):
302323 """Decorator to create subsegment for arbitrary functions
303324
304325 It also captures both response and exceptions as metadata
@@ -317,7 +338,9 @@ def capture_method(self, method: Callable = None, capture_response: bool = True)
317338 method : Callable
318339 Method to annotate on
319340 capture_response : bool, optional
320- Instructs tracer to not include method's response as metadata, by default True
341+ Instructs tracer to not include method's response as metadata
342+ capture_error : bool, optional
343+ Instructs tracer to not include handler's error as metadata, by default True
321344
322345 Example
323346 -------
@@ -449,51 +472,65 @@ async def async_tasks():
449472 # Return a partial function with args filled
450473 if method is None :
451474 logger .debug ("Decorator called with parameters" )
452- return functools .partial (self .capture_method , capture_response = capture_response )
475+ return functools .partial (
476+ self .capture_method , capture_response = capture_response , capture_error = capture_error
477+ )
453478
454479 method_name = f"{ method .__name__ } "
455480
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+
456484 if inspect .iscoroutinefunction (method ):
457485 return self ._decorate_async_function (
458- method = method , capture_response = capture_response , method_name = method_name
486+ method = method , capture_response = capture_response , capture_error = capture_error , method_name = method_name
459487 )
460488 elif inspect .isgeneratorfunction (method ):
461489 return self ._decorate_generator_function (
462- method = method , capture_response = capture_response , method_name = method_name
490+ method = method , capture_response = capture_response , capture_error = capture_error , method_name = method_name
463491 )
464492 elif hasattr (method , "__wrapped__" ) and inspect .isgeneratorfunction (method .__wrapped__ ):
465493 return self ._decorate_generator_function_with_context_manager (
466- method = method , capture_response = capture_response , method_name = method_name
494+ method = method , capture_response = capture_response , capture_error = capture_error , method_name = method_name
467495 )
468496 else :
469497 return self ._decorate_sync_function (
470- method = method , capture_response = capture_response , method_name = method_name
498+ method = method , capture_response = capture_response , capture_error = capture_error , method_name = method_name
471499 )
472500
473- def _decorate_async_function (self , method : Callable = None , capture_response : bool = True , method_name : str = None ):
501+ def _decorate_async_function (
502+ self ,
503+ method : Callable = None ,
504+ capture_response : Optional [bool ] = None ,
505+ capture_error : Optional [bool ] = None ,
506+ method_name : str = None ,
507+ ):
474508 @functools .wraps (method )
475509 async def decorate (* args , ** kwargs ):
476510 async with self .provider .in_subsegment_async (name = f"## { method_name } " ) as subsegment :
477511 try :
478512 logger .debug (f"Calling method: { method_name } " )
479513 response = await method (* args , ** kwargs )
480514 self ._add_response_as_metadata (
481- method_name = method_name ,
482- data = response ,
483- subsegment = subsegment ,
484- capture_response = capture_response ,
515+ method_name = method_name , data = response , subsegment = subsegment , capture_response = capture_response
485516 )
486517 except Exception as err :
487518 logger .exception (f"Exception received from '{ method_name } ' method" )
488- self ._add_full_exception_as_metadata (method_name = method_name , error = err , subsegment = subsegment )
519+ self ._add_full_exception_as_metadata (
520+ method_name = method_name , error = err , subsegment = subsegment , capture_error = capture_error
521+ )
489522 raise
490523
491524 return response
492525
493526 return decorate
494527
495528 def _decorate_generator_function (
496- self , method : Callable = None , capture_response : bool = True , method_name : str = None
529+ self ,
530+ method : Callable = None ,
531+ capture_response : Optional [bool ] = None ,
532+ capture_error : Optional [bool ] = None ,
533+ method_name : str = None ,
497534 ):
498535 @functools .wraps (method )
499536 def decorate (* args , ** kwargs ):
@@ -506,15 +543,21 @@ def decorate(*args, **kwargs):
506543 )
507544 except Exception as err :
508545 logger .exception (f"Exception received from '{ method_name } ' method" )
509- self ._add_full_exception_as_metadata (method_name = method_name , error = err , subsegment = subsegment )
546+ self ._add_full_exception_as_metadata (
547+ method_name = method_name , error = err , subsegment = subsegment , capture_error = capture_error
548+ )
510549 raise
511550
512551 return result
513552
514553 return decorate
515554
516555 def _decorate_generator_function_with_context_manager (
517- self , method : Callable = None , capture_response : bool = True , method_name : str = None
556+ self ,
557+ method : Callable = None ,
558+ capture_response : Optional [bool ] = None ,
559+ capture_error : Optional [bool ] = None ,
560+ method_name : str = None ,
518561 ):
519562 @functools .wraps (method )
520563 @contextlib .contextmanager
@@ -530,12 +573,20 @@ def decorate(*args, **kwargs):
530573 )
531574 except Exception as err :
532575 logger .exception (f"Exception received from '{ method_name } ' method" )
533- self ._add_full_exception_as_metadata (method_name = method_name , error = err , subsegment = subsegment )
576+ self ._add_full_exception_as_metadata (
577+ method_name = method_name , error = err , subsegment = subsegment , capture_error = capture_error
578+ )
534579 raise
535580
536581 return decorate
537582
538- def _decorate_sync_function (self , method : Callable = None , capture_response : bool = True , method_name : str = None ):
583+ def _decorate_sync_function (
584+ self ,
585+ method : Callable = None ,
586+ capture_response : Optional [bool ] = None ,
587+ capture_error : Optional [bool ] = None ,
588+ method_name : str = None ,
589+ ):
539590 @functools .wraps (method )
540591 def decorate (* args , ** kwargs ):
541592 with self .provider .in_subsegment (name = f"## { method_name } " ) as subsegment :
@@ -550,7 +601,9 @@ def decorate(*args, **kwargs):
550601 )
551602 except Exception as err :
552603 logger .exception (f"Exception received from '{ method_name } ' method" )
553- self ._add_full_exception_as_metadata (method_name = method_name , error = err , subsegment = subsegment )
604+ self ._add_full_exception_as_metadata (
605+ method_name = method_name , error = err , subsegment = subsegment , capture_error = capture_error
606+ )
554607 raise
555608
556609 return response
@@ -562,7 +615,7 @@ def _add_response_as_metadata(
562615 method_name : str = None ,
563616 data : Any = None ,
564617 subsegment : aws_xray_sdk .core .models .subsegment = None ,
565- capture_response : bool = True ,
618+ capture_response : Optional [ bool ] = None ,
566619 ):
567620 """Add response as metadata for given subsegment
568621
@@ -575,15 +628,19 @@ def _add_response_as_metadata(
575628 subsegment : aws_xray_sdk.core.models.subsegment, optional
576629 existing subsegment to add metadata on, by default None
577630 capture_response : bool, optional
578- Do not include response as metadata, by default True
631+ Do not include response as metadata
579632 """
580633 if data is None or not capture_response or subsegment is None :
581634 return
582635
583636 subsegment .put_metadata (key = f"{ method_name } response" , value = data , namespace = self ._config ["service" ])
584637
585638 def _add_full_exception_as_metadata (
586- self , method_name : str = None , error : Exception = None , subsegment : aws_xray_sdk .core .models .subsegment = None
639+ self ,
640+ method_name : str = None ,
641+ error : Exception = None ,
642+ subsegment : aws_xray_sdk .core .models .subsegment = None ,
643+ capture_error : Optional [bool ] = None ,
587644 ):
588645 """Add full exception object as metadata for given subsegment
589646
@@ -595,7 +652,12 @@ def _add_full_exception_as_metadata(
595652 error to add as subsegment metadata, by default None
596653 subsegment : aws_xray_sdk.core.models.subsegment, optional
597654 existing subsegment to add metadata on, by default None
655+ capture_error : bool, optional
656+ Do not include error as metadata, by default True
598657 """
658+ if not capture_error :
659+ return
660+
599661 subsegment .put_metadata (key = f"{ method_name } error" , value = error , namespace = self ._config ["service" ])
600662
601663 @staticmethod
0 commit comments