@@ -150,27 +150,27 @@ def __init__(
150
150
self .auto_patch = self ._config ["auto_patch" ]
151
151
152
152
if self .disabled :
153
- self .__disable_tracing_provider ()
153
+ self ._disable_tracer_provider ()
154
154
155
155
if self .auto_patch :
156
156
self .patch (modules = patch_modules )
157
157
158
158
def put_annotation (self , key : str , value : Any ):
159
159
"""Adds annotation to existing segment or subsegment
160
160
161
+ Parameters
162
+ ----------
163
+ key : str
164
+ Annotation key
165
+ value : any
166
+ Value for annotation
167
+
161
168
Example
162
169
-------
163
170
Custom annotation for a pseudo service named payment
164
171
165
172
tracer = Tracer(service="payment")
166
173
tracer.put_annotation("PaymentStatus", "CONFIRMED")
167
-
168
- Parameters
169
- ----------
170
- key : str
171
- Annotation key (e.g. PaymentStatus)
172
- value : any
173
- Value for annotation (e.g. "CONFIRMED")
174
174
"""
175
175
if self .disabled :
176
176
logger .debug ("Tracing has been disabled, aborting put_annotation" )
@@ -226,12 +226,19 @@ def patch(self, modules: Tuple[str] = None):
226
226
else :
227
227
aws_xray_sdk .core .patch (modules )
228
228
229
- def capture_lambda_handler (self , lambda_handler : Callable [[Dict , Any ], Any ] = None ):
229
+ def capture_lambda_handler (self , lambda_handler : Callable [[Dict , Any ], Any ] = None , capture_response : bool = True ):
230
230
"""Decorator to create subsegment for lambda handlers
231
231
232
232
As Lambda follows (event, context) signature we can remove some of the boilerplate
233
233
and also capture any exception any Lambda function throws or its response as metadata
234
234
235
+ Parameters
236
+ ----------
237
+ lambda_handler : Callable
238
+ Method to annotate on
239
+ capture_response : bool, optional
240
+ Instructs tracer to not include handler's response as metadata, by default True
241
+
235
242
Example
236
243
-------
237
244
**Lambda function using capture_lambda_handler decorator**
@@ -241,16 +248,24 @@ def capture_lambda_handler(self, lambda_handler: Callable[[Dict, Any], Any] = No
241
248
def handler(event, context):
242
249
...
243
250
244
- Parameters
245
- ----------
246
- method : Callable
247
- Method to annotate on
251
+ **Preventing Tracer to log response as metadata**
252
+
253
+ tracer = Tracer(service="payment")
254
+ @tracer.capture_lambda_handler(capture_response=False)
255
+ def handler(event, context):
256
+ ...
248
257
249
258
Raises
250
259
------
251
260
err
252
261
Exception raised by method
253
262
"""
263
+ # If handler is None we've been called with parameters
264
+ # Return a partial function with args filled
265
+ if lambda_handler is None :
266
+ logger .debug ("Decorator called with parameters" )
267
+ return functools .partial (self .capture_lambda_handler , capture_response = capture_response )
268
+
254
269
lambda_handler_name = lambda_handler .__name__
255
270
256
271
@functools .wraps (lambda_handler )
@@ -266,22 +281,24 @@ def decorate(event, context):
266
281
logger .debug ("Calling lambda handler" )
267
282
response = lambda_handler (event , context )
268
283
logger .debug ("Received lambda handler response successfully" )
269
- logger .debug (response )
270
284
self ._add_response_as_metadata (
271
- function_name = lambda_handler_name , data = response , subsegment = subsegment
285
+ method_name = lambda_handler_name ,
286
+ data = response ,
287
+ subsegment = subsegment ,
288
+ capture_response = capture_response ,
272
289
)
273
290
except Exception as err :
274
291
logger .exception (f"Exception received from { lambda_handler_name } " )
275
292
self ._add_full_exception_as_metadata (
276
- function_name = lambda_handler_name , error = err , subsegment = subsegment
293
+ method_name = lambda_handler_name , error = err , subsegment = subsegment
277
294
)
278
295
raise
279
296
280
297
return response
281
298
282
299
return decorate
283
300
284
- def capture_method (self , method : Callable = None ):
301
+ def capture_method (self , method : Callable = None , capture_response : bool = True ):
285
302
"""Decorator to create subsegment for arbitrary functions
286
303
287
304
It also captures both response and exceptions as metadata
@@ -295,6 +312,13 @@ def capture_method(self, method: Callable = None):
295
312
`async.gather` is called, or use `in_subsegment_async`
296
313
context manager via our escape hatch mechanism - See examples.
297
314
315
+ Parameters
316
+ ----------
317
+ method : Callable
318
+ Method to annotate on
319
+ capture_response : bool, optional
320
+ Instructs tracer to not include method's response as metadata, by default True
321
+
298
322
Example
299
323
-------
300
324
**Custom function using capture_method decorator**
@@ -416,69 +440,84 @@ async def async_tasks():
416
440
417
441
return { "task": "done", **ret }
418
442
419
- Parameters
420
- ----------
421
- method : Callable
422
- Method to annotate on
423
-
424
443
Raises
425
444
------
426
445
err
427
446
Exception raised by method
428
447
"""
448
+ # If method is None we've been called with parameters
449
+ # Return a partial function with args filled
450
+ if method is None :
451
+ logger .debug ("Decorator called with parameters" )
452
+ return functools .partial (self .capture_method , capture_response = capture_response )
453
+
454
+ method_name = f"{ method .__name__ } "
429
455
430
456
if inspect .iscoroutinefunction (method ):
431
- decorate = self ._decorate_async_function (method = method )
457
+ decorate = self ._decorate_async_function (
458
+ method = method , capture_response = capture_response , method_name = method_name
459
+ )
432
460
elif inspect .isgeneratorfunction (method ):
433
- decorate = self ._decorate_generator_function (method = method )
461
+ decorate = self ._decorate_generator_function (
462
+ method = method , capture_response = capture_response , method_name = method_name
463
+ )
434
464
elif hasattr (method , "__wrapped__" ) and inspect .isgeneratorfunction (method .__wrapped__ ):
435
- decorate = self ._decorate_generator_function_with_context_manager (method = method )
465
+ decorate = self ._decorate_generator_function_with_context_manager (
466
+ method = method , capture_response = capture_response , method_name = method_name
467
+ )
436
468
else :
437
- decorate = self ._decorate_sync_function (method = method )
469
+ decorate = self ._decorate_sync_function (
470
+ method = method , capture_response = capture_response , method_name = method_name
471
+ )
438
472
439
473
return decorate
440
474
441
- def _decorate_async_function (self , method : Callable = None ):
442
- method_name = f"{ method .__name__ } "
443
-
475
+ def _decorate_async_function (self , method : Callable = None , capture_response : bool = True , method_name : str = None ):
444
476
@functools .wraps (method )
445
477
async def decorate (* args , ** kwargs ):
446
478
async with self .provider .in_subsegment_async (name = f"## { method_name } " ) as subsegment :
447
479
try :
448
480
logger .debug (f"Calling method: { method_name } " )
449
481
response = await method (* args , ** kwargs )
450
- self ._add_response_as_metadata (function_name = method_name , data = response , subsegment = subsegment )
482
+ self ._add_response_as_metadata (
483
+ method_name = method_name ,
484
+ data = response ,
485
+ subsegment = subsegment ,
486
+ capture_response = capture_response ,
487
+ )
451
488
except Exception as err :
452
489
logger .exception (f"Exception received from '{ method_name } ' method" )
453
- self ._add_full_exception_as_metadata (function_name = method_name , error = err , subsegment = subsegment )
490
+ self ._add_full_exception_as_metadata (method_name = method_name , error = err , subsegment = subsegment )
454
491
raise
455
492
456
493
return response
457
494
458
495
return decorate
459
496
460
- def _decorate_generator_function (self , method : Callable = None ):
461
- method_name = f" { method . __name__ } "
462
-
497
+ def _decorate_generator_function (
498
+ self , method : Callable = None , capture_response : bool = True , method_name : str = None
499
+ ):
463
500
@functools .wraps (method )
464
501
def decorate (* args , ** kwargs ):
465
502
with self .provider .in_subsegment (name = f"## { method_name } " ) as subsegment :
466
503
try :
467
504
logger .debug (f"Calling method: { method_name } " )
468
505
result = yield from method (* args , ** kwargs )
469
- self ._add_response_as_metadata (function_name = method_name , data = result , subsegment = subsegment )
506
+ self ._add_response_as_metadata (
507
+ method_name = method_name , data = result , subsegment = subsegment , capture_response = capture_response
508
+ )
470
509
except Exception as err :
471
510
logger .exception (f"Exception received from '{ method_name } ' method" )
472
- self ._add_full_exception_as_metadata (function_name = method_name , error = err , subsegment = subsegment )
511
+ self ._add_full_exception_as_metadata (method_name = method_name , error = err , subsegment = subsegment )
473
512
raise
474
513
475
514
return result
476
515
477
516
return decorate
478
517
479
- def _decorate_generator_function_with_context_manager (self , method : Callable = None ):
480
- method_name = f" { method . __name__ } "
481
-
518
+ def _decorate_generator_function_with_context_manager (
519
+ self , method : Callable = None , capture_response : bool = True , method_name : str = None
520
+ ):
482
521
@functools .wraps (method )
483
522
@contextlib .contextmanager
484
523
def decorate (* args , ** kwargs ):
@@ -488,74 +527,87 @@ def decorate(*args, **kwargs):
488
527
with method (* args , ** kwargs ) as return_val :
489
528
result = return_val
490
529
yield result
491
- self ._add_response_as_metadata (function_name = method_name , data = result , subsegment = subsegment )
530
+ self ._add_response_as_metadata (
531
+ method_name = method_name , data = result , subsegment = subsegment , capture_response = capture_response
532
+ )
492
533
except Exception as err :
493
534
logger .exception (f"Exception received from '{ method_name } ' method" )
494
- self ._add_full_exception_as_metadata (function_name = method_name , error = err , subsegment = subsegment )
535
+ self ._add_full_exception_as_metadata (method_name = method_name , error = err , subsegment = subsegment )
495
536
raise
496
537
497
538
return decorate
498
539
499
- def _decorate_sync_function (self , method : Callable = None ):
500
- method_name = f"{ method .__name__ } "
501
-
540
+ def _decorate_sync_function (self , method : Callable = None , capture_response : bool = True , method_name : str = None ):
502
541
@functools .wraps (method )
503
542
def decorate (* args , ** kwargs ):
504
543
with self .provider .in_subsegment (name = f"## { method_name } " ) as subsegment :
505
544
try :
506
545
logger .debug (f"Calling method: { method_name } " )
507
546
response = method (* args , ** kwargs )
508
- self ._add_response_as_metadata (function_name = method_name , data = response , subsegment = subsegment )
547
+ self ._add_response_as_metadata (
548
+ method_name = method_name ,
549
+ data = response ,
550
+ subsegment = subsegment ,
551
+ capture_response = capture_response ,
552
+ )
509
553
except Exception as err :
510
554
logger .exception (f"Exception received from '{ method_name } ' method" )
511
- self ._add_full_exception_as_metadata (function_name = method_name , error = err , subsegment = subsegment )
555
+ self ._add_full_exception_as_metadata (method_name = method_name , error = err , subsegment = subsegment )
512
556
raise
513
557
514
558
return response
515
559
516
560
return decorate
517
561
518
562
def _add_response_as_metadata (
519
- self , function_name : str = None , data : Any = None , subsegment : aws_xray_sdk .core .models .subsegment = None
563
+ self ,
564
+ method_name : str = None ,
565
+ data : Any = None ,
566
+ subsegment : aws_xray_sdk .core .models .subsegment = None ,
567
+ capture_response : bool = True ,
520
568
):
521
569
"""Add response as metadata for given subsegment
522
570
523
571
Parameters
524
572
----------
525
- function_name : str, optional
526
- function name to add as metadata key, by default None
573
+ method_name : str, optional
574
+ method name to add as metadata key, by default None
527
575
data : Any, optional
528
576
data to add as subsegment metadata, by default None
529
577
subsegment : aws_xray_sdk.core.models.subsegment, optional
530
578
existing subsegment to add metadata on, by default None
579
+ capture_response : bool, optional
580
+ Do not include response as metadata, by default True
531
581
"""
532
- if data is None or subsegment is None :
582
+ if data is None or not capture_response or subsegment is None :
533
583
return
534
584
535
- subsegment .put_metadata (key = f"{ function_name } response" , value = data , namespace = self ._config ["service" ])
585
+ subsegment .put_metadata (key = f"{ method_name } response" , value = data , namespace = self ._config ["service" ])
536
586
537
587
def _add_full_exception_as_metadata (
538
- self , function_name : str = None , error : Exception = None , subsegment : aws_xray_sdk .core .models .subsegment = None
588
+ self , method_name : str = None , error : Exception = None , subsegment : aws_xray_sdk .core .models .subsegment = None
539
589
):
540
590
"""Add full exception object as metadata for given subsegment
541
591
542
592
Parameters
543
593
----------
544
- function_name : str, optional
545
- function name to add as metadata key, by default None
594
+ method_name : str, optional
595
+ method name to add as metadata key, by default None
546
596
error : Exception, optional
547
597
error to add as subsegment metadata, by default None
548
598
subsegment : aws_xray_sdk.core.models.subsegment, optional
549
599
existing subsegment to add metadata on, by default None
550
600
"""
551
- subsegment .put_metadata (key = f"{ function_name } error" , value = error , namespace = self ._config ["service" ])
601
+ subsegment .put_metadata (key = f"{ method_name } error" , value = error , namespace = self ._config ["service" ])
552
602
553
- def __disable_tracing_provider (self ):
603
+ @staticmethod
604
+ def _disable_tracer_provider ():
554
605
"""Forcefully disables tracing"""
555
606
logger .debug ("Disabling tracer provider..." )
556
607
aws_xray_sdk .global_sdk_config .set_sdk_enabled (False )
557
608
558
- def __is_trace_disabled (self ) -> bool :
609
+ @staticmethod
610
+ def _is_tracer_disabled () -> bool :
559
611
"""Detects whether trace has been disabled
560
612
561
613
Tracing is automatically disabled in the following conditions:
@@ -592,7 +644,7 @@ def __build_config(
592
644
provider : aws_xray_sdk .core .xray_recorder = None ,
593
645
):
594
646
""" Populates Tracer config for new and existing initializations """
595
- is_disabled = disabled if disabled is not None else self .__is_trace_disabled ()
647
+ is_disabled = disabled if disabled is not None else self ._is_tracer_disabled ()
596
648
is_service = service if service is not None else os .getenv ("POWERTOOLS_SERVICE_NAME" )
597
649
598
650
self ._config ["provider" ] = provider if provider is not None else self ._config ["provider" ]
0 commit comments