sentry-sdk 0.18.0__py2.py3-none-any.whl → 2.46.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. sentry_sdk/__init__.py +48 -6
  2. sentry_sdk/_compat.py +64 -56
  3. sentry_sdk/_init_implementation.py +84 -0
  4. sentry_sdk/_log_batcher.py +172 -0
  5. sentry_sdk/_lru_cache.py +47 -0
  6. sentry_sdk/_metrics_batcher.py +167 -0
  7. sentry_sdk/_queue.py +81 -19
  8. sentry_sdk/_types.py +311 -11
  9. sentry_sdk/_werkzeug.py +98 -0
  10. sentry_sdk/ai/__init__.py +7 -0
  11. sentry_sdk/ai/monitoring.py +137 -0
  12. sentry_sdk/ai/utils.py +144 -0
  13. sentry_sdk/api.py +409 -67
  14. sentry_sdk/attachments.py +75 -0
  15. sentry_sdk/client.py +849 -103
  16. sentry_sdk/consts.py +1389 -34
  17. sentry_sdk/crons/__init__.py +10 -0
  18. sentry_sdk/crons/api.py +62 -0
  19. sentry_sdk/crons/consts.py +4 -0
  20. sentry_sdk/crons/decorator.py +135 -0
  21. sentry_sdk/debug.py +12 -15
  22. sentry_sdk/envelope.py +112 -61
  23. sentry_sdk/feature_flags.py +71 -0
  24. sentry_sdk/hub.py +442 -386
  25. sentry_sdk/integrations/__init__.py +228 -58
  26. sentry_sdk/integrations/_asgi_common.py +108 -0
  27. sentry_sdk/integrations/_wsgi_common.py +131 -40
  28. sentry_sdk/integrations/aiohttp.py +221 -72
  29. sentry_sdk/integrations/anthropic.py +439 -0
  30. sentry_sdk/integrations/argv.py +4 -6
  31. sentry_sdk/integrations/ariadne.py +161 -0
  32. sentry_sdk/integrations/arq.py +247 -0
  33. sentry_sdk/integrations/asgi.py +237 -135
  34. sentry_sdk/integrations/asyncio.py +144 -0
  35. sentry_sdk/integrations/asyncpg.py +208 -0
  36. sentry_sdk/integrations/atexit.py +13 -18
  37. sentry_sdk/integrations/aws_lambda.py +233 -80
  38. sentry_sdk/integrations/beam.py +27 -35
  39. sentry_sdk/integrations/boto3.py +137 -0
  40. sentry_sdk/integrations/bottle.py +91 -69
  41. sentry_sdk/integrations/celery/__init__.py +529 -0
  42. sentry_sdk/integrations/celery/beat.py +293 -0
  43. sentry_sdk/integrations/celery/utils.py +43 -0
  44. sentry_sdk/integrations/chalice.py +35 -28
  45. sentry_sdk/integrations/clickhouse_driver.py +177 -0
  46. sentry_sdk/integrations/cloud_resource_context.py +280 -0
  47. sentry_sdk/integrations/cohere.py +274 -0
  48. sentry_sdk/integrations/dedupe.py +32 -8
  49. sentry_sdk/integrations/django/__init__.py +343 -89
  50. sentry_sdk/integrations/django/asgi.py +201 -22
  51. sentry_sdk/integrations/django/caching.py +204 -0
  52. sentry_sdk/integrations/django/middleware.py +80 -32
  53. sentry_sdk/integrations/django/signals_handlers.py +91 -0
  54. sentry_sdk/integrations/django/templates.py +69 -2
  55. sentry_sdk/integrations/django/transactions.py +39 -14
  56. sentry_sdk/integrations/django/views.py +69 -16
  57. sentry_sdk/integrations/dramatiq.py +226 -0
  58. sentry_sdk/integrations/excepthook.py +19 -13
  59. sentry_sdk/integrations/executing.py +5 -6
  60. sentry_sdk/integrations/falcon.py +128 -65
  61. sentry_sdk/integrations/fastapi.py +141 -0
  62. sentry_sdk/integrations/flask.py +114 -75
  63. sentry_sdk/integrations/gcp.py +67 -36
  64. sentry_sdk/integrations/gnu_backtrace.py +14 -22
  65. sentry_sdk/integrations/google_genai/__init__.py +301 -0
  66. sentry_sdk/integrations/google_genai/consts.py +16 -0
  67. sentry_sdk/integrations/google_genai/streaming.py +155 -0
  68. sentry_sdk/integrations/google_genai/utils.py +576 -0
  69. sentry_sdk/integrations/gql.py +162 -0
  70. sentry_sdk/integrations/graphene.py +151 -0
  71. sentry_sdk/integrations/grpc/__init__.py +168 -0
  72. sentry_sdk/integrations/grpc/aio/__init__.py +7 -0
  73. sentry_sdk/integrations/grpc/aio/client.py +95 -0
  74. sentry_sdk/integrations/grpc/aio/server.py +100 -0
  75. sentry_sdk/integrations/grpc/client.py +91 -0
  76. sentry_sdk/integrations/grpc/consts.py +1 -0
  77. sentry_sdk/integrations/grpc/server.py +66 -0
  78. sentry_sdk/integrations/httpx.py +178 -0
  79. sentry_sdk/integrations/huey.py +174 -0
  80. sentry_sdk/integrations/huggingface_hub.py +378 -0
  81. sentry_sdk/integrations/langchain.py +1132 -0
  82. sentry_sdk/integrations/langgraph.py +337 -0
  83. sentry_sdk/integrations/launchdarkly.py +61 -0
  84. sentry_sdk/integrations/litellm.py +287 -0
  85. sentry_sdk/integrations/litestar.py +315 -0
  86. sentry_sdk/integrations/logging.py +261 -85
  87. sentry_sdk/integrations/loguru.py +213 -0
  88. sentry_sdk/integrations/mcp.py +566 -0
  89. sentry_sdk/integrations/modules.py +6 -33
  90. sentry_sdk/integrations/openai.py +725 -0
  91. sentry_sdk/integrations/openai_agents/__init__.py +61 -0
  92. sentry_sdk/integrations/openai_agents/consts.py +1 -0
  93. sentry_sdk/integrations/openai_agents/patches/__init__.py +5 -0
  94. sentry_sdk/integrations/openai_agents/patches/agent_run.py +140 -0
  95. sentry_sdk/integrations/openai_agents/patches/error_tracing.py +77 -0
  96. sentry_sdk/integrations/openai_agents/patches/models.py +50 -0
  97. sentry_sdk/integrations/openai_agents/patches/runner.py +45 -0
  98. sentry_sdk/integrations/openai_agents/patches/tools.py +77 -0
  99. sentry_sdk/integrations/openai_agents/spans/__init__.py +5 -0
  100. sentry_sdk/integrations/openai_agents/spans/agent_workflow.py +21 -0
  101. sentry_sdk/integrations/openai_agents/spans/ai_client.py +42 -0
  102. sentry_sdk/integrations/openai_agents/spans/execute_tool.py +48 -0
  103. sentry_sdk/integrations/openai_agents/spans/handoff.py +19 -0
  104. sentry_sdk/integrations/openai_agents/spans/invoke_agent.py +86 -0
  105. sentry_sdk/integrations/openai_agents/utils.py +199 -0
  106. sentry_sdk/integrations/openfeature.py +35 -0
  107. sentry_sdk/integrations/opentelemetry/__init__.py +7 -0
  108. sentry_sdk/integrations/opentelemetry/consts.py +5 -0
  109. sentry_sdk/integrations/opentelemetry/integration.py +58 -0
  110. sentry_sdk/integrations/opentelemetry/propagator.py +117 -0
  111. sentry_sdk/integrations/opentelemetry/span_processor.py +391 -0
  112. sentry_sdk/integrations/otlp.py +82 -0
  113. sentry_sdk/integrations/pure_eval.py +20 -11
  114. sentry_sdk/integrations/pydantic_ai/__init__.py +47 -0
  115. sentry_sdk/integrations/pydantic_ai/consts.py +1 -0
  116. sentry_sdk/integrations/pydantic_ai/patches/__init__.py +4 -0
  117. sentry_sdk/integrations/pydantic_ai/patches/agent_run.py +215 -0
  118. sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py +110 -0
  119. sentry_sdk/integrations/pydantic_ai/patches/model_request.py +40 -0
  120. sentry_sdk/integrations/pydantic_ai/patches/tools.py +98 -0
  121. sentry_sdk/integrations/pydantic_ai/spans/__init__.py +3 -0
  122. sentry_sdk/integrations/pydantic_ai/spans/ai_client.py +246 -0
  123. sentry_sdk/integrations/pydantic_ai/spans/execute_tool.py +49 -0
  124. sentry_sdk/integrations/pydantic_ai/spans/invoke_agent.py +112 -0
  125. sentry_sdk/integrations/pydantic_ai/utils.py +223 -0
  126. sentry_sdk/integrations/pymongo.py +214 -0
  127. sentry_sdk/integrations/pyramid.py +71 -60
  128. sentry_sdk/integrations/quart.py +237 -0
  129. sentry_sdk/integrations/ray.py +165 -0
  130. sentry_sdk/integrations/redis/__init__.py +48 -0
  131. sentry_sdk/integrations/redis/_async_common.py +116 -0
  132. sentry_sdk/integrations/redis/_sync_common.py +119 -0
  133. sentry_sdk/integrations/redis/consts.py +19 -0
  134. sentry_sdk/integrations/redis/modules/__init__.py +0 -0
  135. sentry_sdk/integrations/redis/modules/caches.py +118 -0
  136. sentry_sdk/integrations/redis/modules/queries.py +65 -0
  137. sentry_sdk/integrations/redis/rb.py +32 -0
  138. sentry_sdk/integrations/redis/redis.py +69 -0
  139. sentry_sdk/integrations/redis/redis_cluster.py +107 -0
  140. sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +50 -0
  141. sentry_sdk/integrations/redis/utils.py +148 -0
  142. sentry_sdk/integrations/rq.py +62 -52
  143. sentry_sdk/integrations/rust_tracing.py +284 -0
  144. sentry_sdk/integrations/sanic.py +248 -114
  145. sentry_sdk/integrations/serverless.py +13 -22
  146. sentry_sdk/integrations/socket.py +96 -0
  147. sentry_sdk/integrations/spark/spark_driver.py +115 -62
  148. sentry_sdk/integrations/spark/spark_worker.py +42 -50
  149. sentry_sdk/integrations/sqlalchemy.py +82 -37
  150. sentry_sdk/integrations/starlette.py +737 -0
  151. sentry_sdk/integrations/starlite.py +292 -0
  152. sentry_sdk/integrations/statsig.py +37 -0
  153. sentry_sdk/integrations/stdlib.py +100 -58
  154. sentry_sdk/integrations/strawberry.py +394 -0
  155. sentry_sdk/integrations/sys_exit.py +70 -0
  156. sentry_sdk/integrations/threading.py +142 -38
  157. sentry_sdk/integrations/tornado.py +68 -53
  158. sentry_sdk/integrations/trytond.py +15 -20
  159. sentry_sdk/integrations/typer.py +60 -0
  160. sentry_sdk/integrations/unleash.py +33 -0
  161. sentry_sdk/integrations/unraisablehook.py +53 -0
  162. sentry_sdk/integrations/wsgi.py +126 -125
  163. sentry_sdk/logger.py +96 -0
  164. sentry_sdk/metrics.py +81 -0
  165. sentry_sdk/monitor.py +120 -0
  166. sentry_sdk/profiler/__init__.py +49 -0
  167. sentry_sdk/profiler/continuous_profiler.py +730 -0
  168. sentry_sdk/profiler/transaction_profiler.py +839 -0
  169. sentry_sdk/profiler/utils.py +195 -0
  170. sentry_sdk/scope.py +1542 -112
  171. sentry_sdk/scrubber.py +177 -0
  172. sentry_sdk/serializer.py +152 -210
  173. sentry_sdk/session.py +177 -0
  174. sentry_sdk/sessions.py +202 -179
  175. sentry_sdk/spotlight.py +242 -0
  176. sentry_sdk/tracing.py +1202 -294
  177. sentry_sdk/tracing_utils.py +1236 -0
  178. sentry_sdk/transport.py +693 -189
  179. sentry_sdk/types.py +52 -0
  180. sentry_sdk/utils.py +1395 -228
  181. sentry_sdk/worker.py +30 -17
  182. sentry_sdk-2.46.0.dist-info/METADATA +268 -0
  183. sentry_sdk-2.46.0.dist-info/RECORD +189 -0
  184. {sentry_sdk-0.18.0.dist-info → sentry_sdk-2.46.0.dist-info}/WHEEL +1 -1
  185. sentry_sdk-2.46.0.dist-info/entry_points.txt +2 -0
  186. sentry_sdk-2.46.0.dist-info/licenses/LICENSE +21 -0
  187. sentry_sdk/_functools.py +0 -66
  188. sentry_sdk/integrations/celery.py +0 -275
  189. sentry_sdk/integrations/redis.py +0 -103
  190. sentry_sdk-0.18.0.dist-info/LICENSE +0 -9
  191. sentry_sdk-0.18.0.dist-info/METADATA +0 -66
  192. sentry_sdk-0.18.0.dist-info/RECORD +0 -65
  193. {sentry_sdk-0.18.0.dist-info → sentry_sdk-2.46.0.dist-info}/top_level.txt +0 -0
@@ -1,32 +1,40 @@
1
1
  import sys
2
2
  import weakref
3
3
  from inspect import isawaitable
4
+ from urllib.parse import urlsplit
4
5
 
5
- from sentry_sdk._compat import urlparse, reraise
6
- from sentry_sdk.hub import Hub
6
+ import sentry_sdk
7
+ from sentry_sdk import continue_trace
8
+ from sentry_sdk.consts import OP
9
+ from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
10
+ from sentry_sdk.integrations._wsgi_common import RequestExtractor, _filter_headers
11
+ from sentry_sdk.integrations.logging import ignore_logger
12
+ from sentry_sdk.tracing import TransactionSource
7
13
  from sentry_sdk.utils import (
8
14
  capture_internal_exceptions,
15
+ ensure_integration_enabled,
9
16
  event_from_exception,
10
17
  HAS_REAL_CONTEXTVARS,
11
18
  CONTEXTVARS_ERROR_MESSAGE,
19
+ parse_version,
20
+ reraise,
12
21
  )
13
- from sentry_sdk.integrations import Integration, DidNotEnable
14
- from sentry_sdk.integrations._wsgi_common import RequestExtractor, _filter_headers
15
- from sentry_sdk.integrations.logging import ignore_logger
16
22
 
17
- from sentry_sdk._types import MYPY
23
+ from typing import TYPE_CHECKING
18
24
 
19
- if MYPY:
25
+ if TYPE_CHECKING:
26
+ from collections.abc import Container
20
27
  from typing import Any
21
28
  from typing import Callable
22
29
  from typing import Optional
23
30
  from typing import Union
24
- from typing import Tuple
25
31
  from typing import Dict
26
32
 
27
33
  from sanic.request import Request, RequestParameters
34
+ from sanic.response import BaseHTTPResponse
28
35
 
29
- from sentry_sdk._types import Event, EventProcessor, Hint
36
+ from sentry_sdk._types import Event, EventProcessor, ExcInfo, Hint
37
+ from sanic.router import Route
30
38
 
31
39
  try:
32
40
  from sanic import Sanic, __version__ as SANIC_VERSION
@@ -36,20 +44,37 @@ try:
36
44
  except ImportError:
37
45
  raise DidNotEnable("Sanic not installed")
38
46
 
47
+ old_error_handler_lookup = ErrorHandler.lookup
48
+ old_handle_request = Sanic.handle_request
49
+ old_router_get = Router.get
50
+
51
+ try:
52
+ # This method was introduced in Sanic v21.9
53
+ old_startup = Sanic._startup
54
+ except AttributeError:
55
+ pass
56
+
39
57
 
40
58
  class SanicIntegration(Integration):
41
59
  identifier = "sanic"
60
+ origin = f"auto.http.{identifier}"
61
+ version = None
62
+
63
+ def __init__(self, unsampled_statuses=frozenset({404})):
64
+ # type: (Optional[Container[int]]) -> None
65
+ """
66
+ The unsampled_statuses parameter can be used to specify for which HTTP statuses the
67
+ transactions should not be sent to Sentry. By default, transactions are sent for all
68
+ HTTP statuses, except 404. Set unsampled_statuses to None to send transactions for all
69
+ HTTP statuses, including 404.
70
+ """
71
+ self._unsampled_statuses = unsampled_statuses or set()
42
72
 
43
73
  @staticmethod
44
74
  def setup_once():
45
75
  # type: () -> None
46
- try:
47
- version = tuple(map(int, SANIC_VERSION.split(".")))
48
- except (TypeError, ValueError):
49
- raise DidNotEnable("Unparsable Sanic version: {}".format(SANIC_VERSION))
50
-
51
- if version < (0, 8):
52
- raise DidNotEnable("Sanic 0.8 or newer required.")
76
+ SanicIntegration.version = parse_version(SANIC_VERSION)
77
+ _check_minimum_version(SanicIntegration, SanicIntegration.version)
53
78
 
54
79
  if not HAS_REAL_CONTEXTVARS:
55
80
  # We better have contextvars or we're going to leak state between
@@ -71,93 +96,238 @@ class SanicIntegration(Integration):
71
96
  # https://github.com/huge-success/sanic/issues/1332
72
97
  ignore_logger("root")
73
98
 
74
- old_handle_request = Sanic.handle_request
99
+ if SanicIntegration.version is not None and SanicIntegration.version < (21, 9):
100
+ _setup_legacy_sanic()
101
+ return
75
102
 
76
- async def sentry_handle_request(self, request, *args, **kwargs):
77
- # type: (Any, Request, *Any, **Any) -> Any
78
- hub = Hub.current
79
- if hub.get_integration(SanicIntegration) is None:
80
- return old_handle_request(self, request, *args, **kwargs)
103
+ _setup_sanic()
81
104
 
82
- weak_request = weakref.ref(request)
83
105
 
84
- with Hub(hub) as hub:
85
- with hub.configure_scope() as scope:
86
- scope.clear_breadcrumbs()
87
- scope.add_event_processor(_make_request_processor(weak_request))
106
+ class SanicRequestExtractor(RequestExtractor):
107
+ def content_length(self):
108
+ # type: () -> int
109
+ if self.request.body is None:
110
+ return 0
111
+ return len(self.request.body)
88
112
 
89
- response = old_handle_request(self, request, *args, **kwargs)
90
- if isawaitable(response):
91
- response = await response
113
+ def cookies(self):
114
+ # type: () -> Dict[str, str]
115
+ return dict(self.request.cookies)
92
116
 
93
- return response
117
+ def raw_data(self):
118
+ # type: () -> bytes
119
+ return self.request.body
94
120
 
95
- Sanic.handle_request = sentry_handle_request
121
+ def form(self):
122
+ # type: () -> RequestParameters
123
+ return self.request.form
96
124
 
97
- old_router_get = Router.get
125
+ def is_json(self):
126
+ # type: () -> bool
127
+ raise NotImplementedError()
98
128
 
99
- def sentry_router_get(self, request):
100
- # type: (Any, Request) -> Any
101
- rv = old_router_get(self, request)
102
- hub = Hub.current
103
- if hub.get_integration(SanicIntegration) is not None:
104
- with capture_internal_exceptions():
105
- with hub.configure_scope() as scope:
106
- scope.transaction = rv[0].__name__
107
- return rv
129
+ def json(self):
130
+ # type: () -> Optional[Any]
131
+ return self.request.json
108
132
 
109
- Router.get = sentry_router_get
133
+ def files(self):
134
+ # type: () -> RequestParameters
135
+ return self.request.files
110
136
 
111
- old_error_handler_lookup = ErrorHandler.lookup
137
+ def size_of_file(self, file):
138
+ # type: (Any) -> int
139
+ return len(file.body or ())
112
140
 
113
- def sentry_error_handler_lookup(self, exception):
114
- # type: (Any, Exception) -> Optional[object]
115
- _capture_exception(exception)
116
- old_error_handler = old_error_handler_lookup(self, exception)
117
141
 
118
- if old_error_handler is None:
119
- return None
142
+ def _setup_sanic():
143
+ # type: () -> None
144
+ Sanic._startup = _startup
145
+ ErrorHandler.lookup = _sentry_error_handler_lookup
120
146
 
121
- if Hub.current.get_integration(SanicIntegration) is None:
122
- return old_error_handler
123
147
 
124
- async def sentry_wrapped_error_handler(request, exception):
125
- # type: (Request, Exception) -> Any
126
- try:
127
- response = old_error_handler(request, exception)
128
- if isawaitable(response):
129
- response = await response
130
- return response
131
- except Exception:
132
- # Report errors that occur in Sanic error handler. These
133
- # exceptions will not even show up in Sanic's
134
- # `sanic.exceptions` logger.
135
- exc_info = sys.exc_info()
136
- _capture_exception(exc_info)
137
- reraise(*exc_info)
148
+ def _setup_legacy_sanic():
149
+ # type: () -> None
150
+ Sanic.handle_request = _legacy_handle_request
151
+ Router.get = _legacy_router_get
152
+ ErrorHandler.lookup = _sentry_error_handler_lookup
138
153
 
139
- return sentry_wrapped_error_handler
140
154
 
141
- ErrorHandler.lookup = sentry_error_handler_lookup
155
+ async def _startup(self):
156
+ # type: (Sanic) -> None
157
+ # This happens about as early in the lifecycle as possible, just after the
158
+ # Request object is created. The body has not yet been consumed.
159
+ self.signal("http.lifecycle.request")(_context_enter)
142
160
 
161
+ # This happens after the handler is complete. In v21.9 this signal is not
162
+ # dispatched when there is an exception. Therefore we need to close out
163
+ # and call _context_exit from the custom exception handler as well.
164
+ # See https://github.com/sanic-org/sanic/issues/2297
165
+ self.signal("http.lifecycle.response")(_context_exit)
143
166
 
144
- def _capture_exception(exception):
145
- # type: (Union[Tuple[Optional[type], Optional[BaseException], Any], BaseException]) -> None
146
- hub = Hub.current
147
- integration = hub.get_integration(SanicIntegration)
148
- if integration is None:
167
+ # This happens inside of request handling immediately after the route
168
+ # has been identified by the router.
169
+ self.signal("http.routing.after")(_set_transaction)
170
+
171
+ # The above signals need to be declared before this can be called.
172
+ await old_startup(self)
173
+
174
+
175
+ async def _context_enter(request):
176
+ # type: (Request) -> None
177
+ request.ctx._sentry_do_integration = (
178
+ sentry_sdk.get_client().get_integration(SanicIntegration) is not None
179
+ )
180
+
181
+ if not request.ctx._sentry_do_integration:
149
182
  return
150
183
 
151
- # If an integration is there, a client has to be there.
152
- client = hub.client # type: Any
184
+ weak_request = weakref.ref(request)
185
+ request.ctx._sentry_scope = sentry_sdk.isolation_scope()
186
+ scope = request.ctx._sentry_scope.__enter__()
187
+ scope.clear_breadcrumbs()
188
+ scope.add_event_processor(_make_request_processor(weak_request))
189
+
190
+ transaction = continue_trace(
191
+ dict(request.headers),
192
+ op=OP.HTTP_SERVER,
193
+ # Unless the request results in a 404 error, the name and source will get overwritten in _set_transaction
194
+ name=request.path,
195
+ source=TransactionSource.URL,
196
+ origin=SanicIntegration.origin,
197
+ )
198
+ request.ctx._sentry_transaction = sentry_sdk.start_transaction(
199
+ transaction
200
+ ).__enter__()
201
+
202
+
203
+ async def _context_exit(request, response=None):
204
+ # type: (Request, Optional[BaseHTTPResponse]) -> None
205
+ with capture_internal_exceptions():
206
+ if not request.ctx._sentry_do_integration:
207
+ return
208
+
209
+ integration = sentry_sdk.get_client().get_integration(SanicIntegration)
210
+
211
+ response_status = None if response is None else response.status
212
+
213
+ # This capture_internal_exceptions block has been intentionally nested here, so that in case an exception
214
+ # happens while trying to end the transaction, we still attempt to exit the hub.
215
+ with capture_internal_exceptions():
216
+ request.ctx._sentry_transaction.set_http_status(response_status)
217
+ request.ctx._sentry_transaction.sampled &= (
218
+ isinstance(integration, SanicIntegration)
219
+ and response_status not in integration._unsampled_statuses
220
+ )
221
+ request.ctx._sentry_transaction.__exit__(None, None, None)
222
+
223
+ request.ctx._sentry_scope.__exit__(None, None, None)
153
224
 
225
+
226
+ async def _set_transaction(request, route, **_):
227
+ # type: (Request, Route, **Any) -> None
228
+ if request.ctx._sentry_do_integration:
229
+ with capture_internal_exceptions():
230
+ scope = sentry_sdk.get_current_scope()
231
+ route_name = route.name.replace(request.app.name, "").strip(".")
232
+ scope.set_transaction_name(route_name, source=TransactionSource.COMPONENT)
233
+
234
+
235
+ def _sentry_error_handler_lookup(self, exception, *args, **kwargs):
236
+ # type: (Any, Exception, *Any, **Any) -> Optional[object]
237
+ _capture_exception(exception)
238
+ old_error_handler = old_error_handler_lookup(self, exception, *args, **kwargs)
239
+
240
+ if old_error_handler is None:
241
+ return None
242
+
243
+ if sentry_sdk.get_client().get_integration(SanicIntegration) is None:
244
+ return old_error_handler
245
+
246
+ async def sentry_wrapped_error_handler(request, exception):
247
+ # type: (Request, Exception) -> Any
248
+ try:
249
+ response = old_error_handler(request, exception)
250
+ if isawaitable(response):
251
+ response = await response
252
+ return response
253
+ except Exception:
254
+ # Report errors that occur in Sanic error handler. These
255
+ # exceptions will not even show up in Sanic's
256
+ # `sanic.exceptions` logger.
257
+ exc_info = sys.exc_info()
258
+ _capture_exception(exc_info)
259
+ reraise(*exc_info)
260
+ finally:
261
+ # As mentioned in previous comment in _startup, this can be removed
262
+ # after https://github.com/sanic-org/sanic/issues/2297 is resolved
263
+ if SanicIntegration.version and SanicIntegration.version == (21, 9):
264
+ await _context_exit(request)
265
+
266
+ return sentry_wrapped_error_handler
267
+
268
+
269
+ async def _legacy_handle_request(self, request, *args, **kwargs):
270
+ # type: (Any, Request, *Any, **Any) -> Any
271
+ if sentry_sdk.get_client().get_integration(SanicIntegration) is None:
272
+ return await old_handle_request(self, request, *args, **kwargs)
273
+
274
+ weak_request = weakref.ref(request)
275
+
276
+ with sentry_sdk.isolation_scope() as scope:
277
+ scope.clear_breadcrumbs()
278
+ scope.add_event_processor(_make_request_processor(weak_request))
279
+
280
+ response = old_handle_request(self, request, *args, **kwargs)
281
+ if isawaitable(response):
282
+ response = await response
283
+
284
+ return response
285
+
286
+
287
+ def _legacy_router_get(self, *args):
288
+ # type: (Any, Union[Any, Request]) -> Any
289
+ rv = old_router_get(self, *args)
290
+ if sentry_sdk.get_client().get_integration(SanicIntegration) is not None:
291
+ with capture_internal_exceptions():
292
+ scope = sentry_sdk.get_isolation_scope()
293
+ if SanicIntegration.version and SanicIntegration.version >= (21, 3):
294
+ # Sanic versions above and including 21.3 append the app name to the
295
+ # route name, and so we need to remove it from Route name so the
296
+ # transaction name is consistent across all versions
297
+ sanic_app_name = self.ctx.app.name
298
+ sanic_route = rv[0].name
299
+
300
+ if sanic_route.startswith("%s." % sanic_app_name):
301
+ # We add a 1 to the len of the sanic_app_name because there is a dot
302
+ # that joins app name and the route name
303
+ # Format: app_name.route_name
304
+ sanic_route = sanic_route[len(sanic_app_name) + 1 :]
305
+
306
+ scope.set_transaction_name(
307
+ sanic_route, source=TransactionSource.COMPONENT
308
+ )
309
+ else:
310
+ scope.set_transaction_name(
311
+ rv[0].__name__, source=TransactionSource.COMPONENT
312
+ )
313
+
314
+ return rv
315
+
316
+
317
+ @ensure_integration_enabled(SanicIntegration)
318
+ def _capture_exception(exception):
319
+ # type: (Union[ExcInfo, BaseException]) -> None
154
320
  with capture_internal_exceptions():
155
321
  event, hint = event_from_exception(
156
322
  exception,
157
- client_options=client.options,
323
+ client_options=sentry_sdk.get_client().options,
158
324
  mechanism={"type": "sanic", "handled": False},
159
325
  )
160
- hub.capture_event(event, hint=hint)
326
+
327
+ if hint and hasattr(hint["exc_info"][0], "quiet") and hint["exc_info"][0].quiet:
328
+ return
329
+
330
+ sentry_sdk.capture_event(event, hint=hint)
161
331
 
162
332
 
163
333
  def _make_request_processor(weak_request):
@@ -180,7 +350,7 @@ def _make_request_processor(weak_request):
180
350
  extractor.extract_into_event(event)
181
351
 
182
352
  request_info = event["request"]
183
- urlparts = urlparse.urlsplit(request.url)
353
+ urlparts = urlsplit(request.url)
184
354
 
185
355
  request_info["url"] = "%s://%s%s" % (
186
356
  urlparts.scheme,
@@ -196,39 +366,3 @@ def _make_request_processor(weak_request):
196
366
  return event
197
367
 
198
368
  return sanic_processor
199
-
200
-
201
- class SanicRequestExtractor(RequestExtractor):
202
- def content_length(self):
203
- # type: () -> int
204
- if self.request.body is None:
205
- return 0
206
- return len(self.request.body)
207
-
208
- def cookies(self):
209
- # type: () -> Dict[str, str]
210
- return dict(self.request.cookies)
211
-
212
- def raw_data(self):
213
- # type: () -> bytes
214
- return self.request.body
215
-
216
- def form(self):
217
- # type: () -> RequestParameters
218
- return self.request.form
219
-
220
- def is_json(self):
221
- # type: () -> bool
222
- raise NotImplementedError()
223
-
224
- def json(self):
225
- # type: () -> Optional[Any]
226
- return self.request.json
227
-
228
- def files(self):
229
- # type: () -> RequestParameters
230
- return self.request.files
231
-
232
- def size_of_file(self, file):
233
- # type: (Any) -> int
234
- return len(file.body or ())
@@ -1,20 +1,17 @@
1
1
  import sys
2
+ from functools import wraps
2
3
 
3
- from sentry_sdk.hub import Hub
4
- from sentry_sdk.utils import event_from_exception
5
- from sentry_sdk._compat import reraise
6
- from sentry_sdk._functools import wraps
4
+ import sentry_sdk
5
+ from sentry_sdk.utils import event_from_exception, reraise
7
6
 
7
+ from typing import TYPE_CHECKING
8
8
 
9
- from sentry_sdk._types import MYPY
10
-
11
- if MYPY:
9
+ if TYPE_CHECKING:
12
10
  from typing import Any
13
11
  from typing import Callable
14
12
  from typing import TypeVar
15
13
  from typing import Union
16
14
  from typing import Optional
17
-
18
15
  from typing import overload
19
16
 
20
17
  F = TypeVar("F", bound=Callable[..., Any])
@@ -27,7 +24,7 @@ else:
27
24
 
28
25
 
29
26
  @overload
30
- def serverless_function(f, flush=True): # noqa: F811
27
+ def serverless_function(f, flush=True):
31
28
  # type: (F, bool) -> F
32
29
  pass
33
30
 
@@ -45,9 +42,8 @@ def serverless_function(f=None, flush=True): # noqa
45
42
  @wraps(f)
46
43
  def inner(*args, **kwargs):
47
44
  # type: (*Any, **Any) -> Any
48
- with Hub(Hub.current) as hub:
49
- with hub.configure_scope() as scope:
50
- scope.clear_breadcrumbs()
45
+ with sentry_sdk.isolation_scope() as scope:
46
+ scope.clear_breadcrumbs()
51
47
 
52
48
  try:
53
49
  return f(*args, **kwargs)
@@ -55,7 +51,7 @@ def serverless_function(f=None, flush=True): # noqa
55
51
  _capture_and_reraise()
56
52
  finally:
57
53
  if flush:
58
- _flush_client()
54
+ sentry_sdk.flush()
59
55
 
60
56
  return inner # type: ignore
61
57
 
@@ -68,18 +64,13 @@ def serverless_function(f=None, flush=True): # noqa
68
64
  def _capture_and_reraise():
69
65
  # type: () -> None
70
66
  exc_info = sys.exc_info()
71
- hub = Hub.current
72
- if hub.client is not None:
67
+ client = sentry_sdk.get_client()
68
+ if client.is_active():
73
69
  event, hint = event_from_exception(
74
70
  exc_info,
75
- client_options=hub.client.options,
71
+ client_options=client.options,
76
72
  mechanism={"type": "serverless", "handled": False},
77
73
  )
78
- hub.capture_event(event, hint=hint)
74
+ sentry_sdk.capture_event(event, hint=hint)
79
75
 
80
76
  reraise(*exc_info)
81
-
82
-
83
- def _flush_client():
84
- # type: () -> None
85
- return Hub.current.flush()
@@ -0,0 +1,96 @@
1
+ import socket
2
+
3
+ import sentry_sdk
4
+ from sentry_sdk._types import MYPY
5
+ from sentry_sdk.consts import OP
6
+ from sentry_sdk.integrations import Integration
7
+
8
+ if MYPY:
9
+ from socket import AddressFamily, SocketKind
10
+ from typing import Tuple, Optional, Union, List
11
+
12
+ __all__ = ["SocketIntegration"]
13
+
14
+
15
+ class SocketIntegration(Integration):
16
+ identifier = "socket"
17
+ origin = f"auto.socket.{identifier}"
18
+
19
+ @staticmethod
20
+ def setup_once():
21
+ # type: () -> None
22
+ """
23
+ patches two of the most used functions of socket: create_connection and getaddrinfo(dns resolver)
24
+ """
25
+ _patch_create_connection()
26
+ _patch_getaddrinfo()
27
+
28
+
29
+ def _get_span_description(host, port):
30
+ # type: (Union[bytes, str, None], Union[bytes, str, int, None]) -> str
31
+
32
+ try:
33
+ host = host.decode() # type: ignore
34
+ except (UnicodeDecodeError, AttributeError):
35
+ pass
36
+
37
+ try:
38
+ port = port.decode() # type: ignore
39
+ except (UnicodeDecodeError, AttributeError):
40
+ pass
41
+
42
+ description = "%s:%s" % (host, port) # type: ignore
43
+ return description
44
+
45
+
46
+ def _patch_create_connection():
47
+ # type: () -> None
48
+ real_create_connection = socket.create_connection
49
+
50
+ def create_connection(
51
+ address,
52
+ timeout=socket._GLOBAL_DEFAULT_TIMEOUT, # type: ignore
53
+ source_address=None,
54
+ ):
55
+ # type: (Tuple[Optional[str], int], Optional[float], Optional[Tuple[Union[bytearray, bytes, str], int]])-> socket.socket
56
+ integration = sentry_sdk.get_client().get_integration(SocketIntegration)
57
+ if integration is None:
58
+ return real_create_connection(address, timeout, source_address)
59
+
60
+ with sentry_sdk.start_span(
61
+ op=OP.SOCKET_CONNECTION,
62
+ name=_get_span_description(address[0], address[1]),
63
+ origin=SocketIntegration.origin,
64
+ ) as span:
65
+ span.set_data("address", address)
66
+ span.set_data("timeout", timeout)
67
+ span.set_data("source_address", source_address)
68
+
69
+ return real_create_connection(
70
+ address=address, timeout=timeout, source_address=source_address
71
+ )
72
+
73
+ socket.create_connection = create_connection # type: ignore
74
+
75
+
76
+ def _patch_getaddrinfo():
77
+ # type: () -> None
78
+ real_getaddrinfo = socket.getaddrinfo
79
+
80
+ def getaddrinfo(host, port, family=0, type=0, proto=0, flags=0):
81
+ # type: (Union[bytes, str, None], Union[bytes, str, int, None], int, int, int, int) -> List[Tuple[AddressFamily, SocketKind, int, str, Union[Tuple[str, int], Tuple[str, int, int, int], Tuple[int, bytes]]]]
82
+ integration = sentry_sdk.get_client().get_integration(SocketIntegration)
83
+ if integration is None:
84
+ return real_getaddrinfo(host, port, family, type, proto, flags)
85
+
86
+ with sentry_sdk.start_span(
87
+ op=OP.SOCKET_DNS,
88
+ name=_get_span_description(host, port),
89
+ origin=SocketIntegration.origin,
90
+ ) as span:
91
+ span.set_data("host", host)
92
+ span.set_data("port", port)
93
+
94
+ return real_getaddrinfo(host, port, family, type, proto, flags)
95
+
96
+ socket.getaddrinfo = getaddrinfo