Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/sentry_sdk/integrations/boto3.py: 13%

78 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1from __future__ import absolute_import 

2 

3from sentry_sdk import Hub 

4from sentry_sdk.integrations import Integration, DidNotEnable 

5from sentry_sdk.tracing import Span 

6 

7from sentry_sdk._functools import partial 

8from sentry_sdk._types import MYPY 

9 

10if MYPY: 10 ↛ 11line 10 didn't jump to line 11, because the condition on line 10 was never true

11 from typing import Any 

12 from typing import Dict 

13 from typing import Optional 

14 from typing import Type 

15 

16try: 

17 from botocore import __version__ as BOTOCORE_VERSION # type: ignore 

18 from botocore.client import BaseClient # type: ignore 

19 from botocore.response import StreamingBody # type: ignore 

20 from botocore.awsrequest import AWSRequest # type: ignore 

21except ImportError: 

22 raise DidNotEnable("botocore is not installed") 

23 

24 

25class Boto3Integration(Integration): 

26 identifier = "boto3" 

27 

28 @staticmethod 

29 def setup_once(): 

30 # type: () -> None 

31 try: 

32 version = tuple(map(int, BOTOCORE_VERSION.split(".")[:3])) 

33 except (ValueError, TypeError): 

34 raise DidNotEnable( 

35 "Unparsable botocore version: {}".format(BOTOCORE_VERSION) 

36 ) 

37 if version < (1, 12): 

38 raise DidNotEnable("Botocore 1.12 or newer is required.") 

39 orig_init = BaseClient.__init__ 

40 

41 def sentry_patched_init(self, *args, **kwargs): 

42 # type: (Type[BaseClient], *Any, **Any) -> None 

43 orig_init(self, *args, **kwargs) 

44 meta = self.meta 

45 service_id = meta.service_model.service_id.hyphenize() 

46 meta.events.register( 

47 "request-created", 

48 partial(_sentry_request_created, service_id=service_id), 

49 ) 

50 meta.events.register("after-call", _sentry_after_call) 

51 meta.events.register("after-call-error", _sentry_after_call_error) 

52 

53 BaseClient.__init__ = sentry_patched_init 

54 

55 

56def _sentry_request_created(service_id, request, operation_name, **kwargs): 

57 # type: (str, AWSRequest, str, **Any) -> None 

58 hub = Hub.current 

59 if hub.get_integration(Boto3Integration) is None: 

60 return 

61 

62 description = "aws.%s.%s" % (service_id, operation_name) 

63 span = hub.start_span( 

64 hub=hub, 

65 op="aws.request", 

66 description=description, 

67 ) 

68 span.set_tag("aws.service_id", service_id) 

69 span.set_tag("aws.operation_name", operation_name) 

70 span.set_data("aws.request.url", request.url) 

71 

72 # We do it in order for subsequent http calls/retries be 

73 # attached to this span. 

74 span.__enter__() 

75 

76 # request.context is an open-ended data-structure 

77 # where we can add anything useful in request life cycle. 

78 request.context["_sentrysdk_span"] = span 

79 

80 

81def _sentry_after_call(context, parsed, **kwargs): 

82 # type: (Dict[str, Any], Dict[str, Any], **Any) -> None 

83 span = context.pop("_sentrysdk_span", None) # type: Optional[Span] 

84 

85 # Span could be absent if the integration is disabled. 

86 if span is None: 

87 return 

88 span.__exit__(None, None, None) 

89 

90 body = parsed.get("Body") 

91 if not isinstance(body, StreamingBody): 

92 return 

93 

94 streaming_span = span.start_child( 

95 op="aws.request.stream", 

96 description=span.description, 

97 ) 

98 

99 orig_read = body.read 

100 orig_close = body.close 

101 

102 def sentry_streaming_body_read(*args, **kwargs): 

103 # type: (*Any, **Any) -> bytes 

104 try: 

105 ret = orig_read(*args, **kwargs) 

106 if not ret: 

107 streaming_span.finish() 

108 return ret 

109 except Exception: 

110 streaming_span.finish() 

111 raise 

112 

113 body.read = sentry_streaming_body_read 

114 

115 def sentry_streaming_body_close(*args, **kwargs): 

116 # type: (*Any, **Any) -> None 

117 streaming_span.finish() 

118 orig_close(*args, **kwargs) 

119 

120 body.close = sentry_streaming_body_close 

121 

122 

123def _sentry_after_call_error(context, exception, **kwargs): 

124 # type: (Dict[str, Any], Type[BaseException], **Any) -> None 

125 span = context.pop("_sentrysdk_span", None) # type: Optional[Span] 

126 

127 # Span could be absent if the integration is disabled. 

128 if span is None: 

129 return 

130 span.__exit__(type(exception), exception, None)