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
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1from __future__ import absolute_import
3from sentry_sdk import Hub
4from sentry_sdk.integrations import Integration, DidNotEnable
5from sentry_sdk.tracing import Span
7from sentry_sdk._functools import partial
8from sentry_sdk._types import MYPY
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
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")
25class Boto3Integration(Integration):
26 identifier = "boto3"
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__
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)
53 BaseClient.__init__ = sentry_patched_init
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
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)
72 # We do it in order for subsequent http calls/retries be
73 # attached to this span.
74 span.__enter__()
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
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]
85 # Span could be absent if the integration is disabled.
86 if span is None:
87 return
88 span.__exit__(None, None, None)
90 body = parsed.get("Body")
91 if not isinstance(body, StreamingBody):
92 return
94 streaming_span = span.start_child(
95 op="aws.request.stream",
96 description=span.description,
97 )
99 orig_read = body.read
100 orig_close = body.close
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
113 body.read = sentry_streaming_body_read
115 def sentry_streaming_body_close(*args, **kwargs):
116 # type: (*Any, **Any) -> None
117 streaming_span.finish()
118 orig_close(*args, **kwargs)
120 body.close = sentry_streaming_body_close
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]
127 # Span could be absent if the integration is disabled.
128 if span is None:
129 return
130 span.__exit__(type(exception), exception, None)