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
@@ -0,0 +1,242 @@
1
+ import io
2
+ import logging
3
+ import os
4
+ import urllib.parse
5
+ import urllib.request
6
+ import urllib.error
7
+ import urllib3
8
+ import sys
9
+
10
+ from itertools import chain, product
11
+
12
+ from typing import TYPE_CHECKING
13
+
14
+ if TYPE_CHECKING:
15
+ from typing import Any
16
+ from typing import Callable
17
+ from typing import Dict
18
+ from typing import Optional
19
+ from typing import Self
20
+
21
+ from sentry_sdk.utils import (
22
+ logger as sentry_logger,
23
+ env_to_bool,
24
+ capture_internal_exceptions,
25
+ )
26
+ from sentry_sdk.envelope import Envelope
27
+
28
+
29
+ logger = logging.getLogger("spotlight")
30
+
31
+
32
+ DEFAULT_SPOTLIGHT_URL = "http://localhost:8969/stream"
33
+ DJANGO_SPOTLIGHT_MIDDLEWARE_PATH = "sentry_sdk.spotlight.SpotlightMiddleware"
34
+
35
+
36
+ class SpotlightClient:
37
+ def __init__(self, url):
38
+ # type: (str) -> None
39
+ self.url = url
40
+ self.http = urllib3.PoolManager()
41
+ self.fails = 0
42
+
43
+ def capture_envelope(self, envelope):
44
+ # type: (Envelope) -> None
45
+ body = io.BytesIO()
46
+ envelope.serialize_into(body)
47
+ try:
48
+ req = self.http.request(
49
+ url=self.url,
50
+ body=body.getvalue(),
51
+ method="POST",
52
+ headers={
53
+ "Content-Type": "application/x-sentry-envelope",
54
+ },
55
+ )
56
+ req.close()
57
+ self.fails = 0
58
+ except Exception as e:
59
+ if self.fails < 2:
60
+ sentry_logger.warning(str(e))
61
+ self.fails += 1
62
+ elif self.fails == 2:
63
+ self.fails += 1
64
+ sentry_logger.warning(
65
+ "Looks like Spotlight is not running, will keep trying to send events but will not log errors."
66
+ )
67
+ # omitting self.fails += 1 in the `else:` case intentionally
68
+ # to avoid overflowing the variable if Spotlight never becomes reachable
69
+
70
+
71
+ try:
72
+ from django.utils.deprecation import MiddlewareMixin
73
+ from django.http import HttpResponseServerError, HttpResponse, HttpRequest
74
+ from django.conf import settings
75
+
76
+ SPOTLIGHT_JS_ENTRY_PATH = "/assets/main.js"
77
+ SPOTLIGHT_JS_SNIPPET_PATTERN = (
78
+ "<script>window.__spotlight = {{ initOptions: {{ sidecarUrl: '{spotlight_url}', fullPage: false }} }};</script>\n"
79
+ '<script type="module" crossorigin src="{spotlight_js_url}"></script>\n'
80
+ )
81
+ SPOTLIGHT_ERROR_PAGE_SNIPPET = (
82
+ '<html><base href="{spotlight_url}">\n'
83
+ '<script>window.__spotlight = {{ initOptions: {{ fullPage: true, startFrom: "/errors/{event_id}" }}}};</script>\n'
84
+ )
85
+ CHARSET_PREFIX = "charset="
86
+ BODY_TAG_NAME = "body"
87
+ BODY_CLOSE_TAG_POSSIBILITIES = tuple(
88
+ "</{}>".format("".join(chars))
89
+ for chars in product(*zip(BODY_TAG_NAME.upper(), BODY_TAG_NAME.lower()))
90
+ )
91
+
92
+ class SpotlightMiddleware(MiddlewareMixin): # type: ignore[misc]
93
+ _spotlight_script = None # type: Optional[str]
94
+ _spotlight_url = None # type: Optional[str]
95
+
96
+ def __init__(self, get_response):
97
+ # type: (Self, Callable[..., HttpResponse]) -> None
98
+ super().__init__(get_response)
99
+
100
+ import sentry_sdk.api
101
+
102
+ self.sentry_sdk = sentry_sdk.api
103
+
104
+ spotlight_client = self.sentry_sdk.get_client().spotlight
105
+ if spotlight_client is None:
106
+ sentry_logger.warning(
107
+ "Cannot find Spotlight client from SpotlightMiddleware, disabling the middleware."
108
+ )
109
+ return None
110
+ # Spotlight URL has a trailing `/stream` part at the end so split it off
111
+ self._spotlight_url = urllib.parse.urljoin(spotlight_client.url, "../")
112
+
113
+ @property
114
+ def spotlight_script(self):
115
+ # type: (Self) -> Optional[str]
116
+ if self._spotlight_url is not None and self._spotlight_script is None:
117
+ try:
118
+ spotlight_js_url = urllib.parse.urljoin(
119
+ self._spotlight_url, SPOTLIGHT_JS_ENTRY_PATH
120
+ )
121
+ req = urllib.request.Request(
122
+ spotlight_js_url,
123
+ method="HEAD",
124
+ )
125
+ urllib.request.urlopen(req)
126
+ self._spotlight_script = SPOTLIGHT_JS_SNIPPET_PATTERN.format(
127
+ spotlight_url=self._spotlight_url,
128
+ spotlight_js_url=spotlight_js_url,
129
+ )
130
+ except urllib.error.URLError as err:
131
+ sentry_logger.debug(
132
+ "Cannot get Spotlight JS to inject at %s. SpotlightMiddleware will not be very useful.",
133
+ spotlight_js_url,
134
+ exc_info=err,
135
+ )
136
+
137
+ return self._spotlight_script
138
+
139
+ def process_response(self, _request, response):
140
+ # type: (Self, HttpRequest, HttpResponse) -> Optional[HttpResponse]
141
+ content_type_header = tuple(
142
+ p.strip()
143
+ for p in response.headers.get("Content-Type", "").lower().split(";")
144
+ )
145
+ content_type = content_type_header[0]
146
+ if len(content_type_header) > 1 and content_type_header[1].startswith(
147
+ CHARSET_PREFIX
148
+ ):
149
+ encoding = content_type_header[1][len(CHARSET_PREFIX) :]
150
+ else:
151
+ encoding = "utf-8"
152
+
153
+ if (
154
+ self.spotlight_script is not None
155
+ and not response.streaming
156
+ and content_type == "text/html"
157
+ ):
158
+ content_length = len(response.content)
159
+ injection = self.spotlight_script.encode(encoding)
160
+ injection_site = next(
161
+ (
162
+ idx
163
+ for idx in (
164
+ response.content.rfind(body_variant.encode(encoding))
165
+ for body_variant in BODY_CLOSE_TAG_POSSIBILITIES
166
+ )
167
+ if idx > -1
168
+ ),
169
+ content_length,
170
+ )
171
+
172
+ # This approach works even when we don't have a `</body>` tag
173
+ response.content = (
174
+ response.content[:injection_site]
175
+ + injection
176
+ + response.content[injection_site:]
177
+ )
178
+
179
+ if response.has_header("Content-Length"):
180
+ response.headers["Content-Length"] = content_length + len(injection)
181
+
182
+ return response
183
+
184
+ def process_exception(self, _request, exception):
185
+ # type: (Self, HttpRequest, Exception) -> Optional[HttpResponseServerError]
186
+ if not settings.DEBUG or not self._spotlight_url:
187
+ return None
188
+
189
+ try:
190
+ spotlight = (
191
+ urllib.request.urlopen(self._spotlight_url).read().decode("utf-8")
192
+ )
193
+ except urllib.error.URLError:
194
+ return None
195
+ else:
196
+ event_id = self.sentry_sdk.capture_exception(exception)
197
+ return HttpResponseServerError(
198
+ spotlight.replace(
199
+ "<html>",
200
+ SPOTLIGHT_ERROR_PAGE_SNIPPET.format(
201
+ spotlight_url=self._spotlight_url, event_id=event_id
202
+ ),
203
+ )
204
+ )
205
+
206
+ except ImportError:
207
+ settings = None
208
+
209
+
210
+ def setup_spotlight(options):
211
+ # type: (Dict[str, Any]) -> Optional[SpotlightClient]
212
+ _handler = logging.StreamHandler(sys.stderr)
213
+ _handler.setFormatter(logging.Formatter(" [spotlight] %(levelname)s: %(message)s"))
214
+ logger.addHandler(_handler)
215
+ logger.setLevel(logging.INFO)
216
+
217
+ url = options.get("spotlight")
218
+
219
+ if url is True:
220
+ url = DEFAULT_SPOTLIGHT_URL
221
+
222
+ if not isinstance(url, str):
223
+ return None
224
+
225
+ with capture_internal_exceptions():
226
+ if (
227
+ settings is not None
228
+ and settings.DEBUG
229
+ and env_to_bool(os.environ.get("SENTRY_SPOTLIGHT_ON_ERROR", "1"))
230
+ and env_to_bool(os.environ.get("SENTRY_SPOTLIGHT_MIDDLEWARE", "1"))
231
+ ):
232
+ middleware = settings.MIDDLEWARE
233
+ if DJANGO_SPOTLIGHT_MIDDLEWARE_PATH not in middleware:
234
+ settings.MIDDLEWARE = type(middleware)(
235
+ chain(middleware, (DJANGO_SPOTLIGHT_MIDDLEWARE_PATH,))
236
+ )
237
+ logger.info("Enabled Spotlight integration for Django")
238
+
239
+ client = SpotlightClient(url)
240
+ logger.info("Enabled Spotlight using sidecar at %s", url)
241
+
242
+ return client