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,15 +1,15 @@
1
- from sentry_sdk import configure_scope
2
- from sentry_sdk.hub import Hub
1
+ import sentry_sdk
3
2
  from sentry_sdk.integrations import Integration
4
- from sentry_sdk.utils import capture_internal_exceptions
3
+ from sentry_sdk.utils import capture_internal_exceptions, ensure_integration_enabled
5
4
 
6
- from sentry_sdk._types import MYPY
5
+ from typing import TYPE_CHECKING
7
6
 
8
- if MYPY:
7
+ if TYPE_CHECKING:
9
8
  from typing import Any
10
9
  from typing import Optional
11
10
 
12
11
  from sentry_sdk._types import Event, Hint
12
+ from pyspark import SparkContext
13
13
 
14
14
 
15
15
  class SparkIntegration(Integration):
@@ -18,7 +18,7 @@ class SparkIntegration(Integration):
18
18
  @staticmethod
19
19
  def setup_once():
20
20
  # type: () -> None
21
- patch_spark_context_init()
21
+ _setup_sentry_tracing()
22
22
 
23
23
 
24
24
  def _set_app_properties():
@@ -31,14 +31,18 @@ def _set_app_properties():
31
31
 
32
32
  spark_context = SparkContext._active_spark_context
33
33
  if spark_context:
34
- spark_context.setLocalProperty("sentry_app_name", spark_context.appName)
35
34
  spark_context.setLocalProperty(
36
- "sentry_application_id", spark_context.applicationId
35
+ "sentry_app_name",
36
+ spark_context.appName,
37
+ )
38
+ spark_context.setLocalProperty(
39
+ "sentry_application_id",
40
+ spark_context.applicationId,
37
41
  )
38
42
 
39
43
 
40
44
  def _start_sentry_listener(sc):
41
- # type: (Any) -> None
45
+ # type: (SparkContext) -> None
42
46
  """
43
47
  Start java gateway server to add custom `SparkListener`
44
48
  """
@@ -50,62 +54,77 @@ def _start_sentry_listener(sc):
50
54
  sc._jsc.sc().addSparkListener(listener)
51
55
 
52
56
 
53
- def patch_spark_context_init():
57
+ def _add_event_processor(sc):
58
+ # type: (SparkContext) -> None
59
+ scope = sentry_sdk.get_isolation_scope()
60
+
61
+ @scope.add_event_processor
62
+ def process_event(event, hint):
63
+ # type: (Event, Hint) -> Optional[Event]
64
+ with capture_internal_exceptions():
65
+ if sentry_sdk.get_client().get_integration(SparkIntegration) is None:
66
+ return event
67
+
68
+ if sc._active_spark_context is None:
69
+ return event
70
+
71
+ event.setdefault("user", {}).setdefault("id", sc.sparkUser())
72
+
73
+ event.setdefault("tags", {}).setdefault(
74
+ "executor.id", sc._conf.get("spark.executor.id")
75
+ )
76
+ event["tags"].setdefault(
77
+ "spark-submit.deployMode",
78
+ sc._conf.get("spark.submit.deployMode"),
79
+ )
80
+ event["tags"].setdefault("driver.host", sc._conf.get("spark.driver.host"))
81
+ event["tags"].setdefault("driver.port", sc._conf.get("spark.driver.port"))
82
+ event["tags"].setdefault("spark_version", sc.version)
83
+ event["tags"].setdefault("app_name", sc.appName)
84
+ event["tags"].setdefault("application_id", sc.applicationId)
85
+ event["tags"].setdefault("master", sc.master)
86
+ event["tags"].setdefault("spark_home", sc.sparkHome)
87
+
88
+ event.setdefault("extra", {}).setdefault("web_url", sc.uiWebUrl)
89
+
90
+ return event
91
+
92
+
93
+ def _activate_integration(sc):
94
+ # type: (SparkContext) -> None
95
+
96
+ _start_sentry_listener(sc)
97
+ _set_app_properties()
98
+ _add_event_processor(sc)
99
+
100
+
101
+ def _patch_spark_context_init():
54
102
  # type: () -> None
55
103
  from pyspark import SparkContext
56
104
 
57
105
  spark_context_init = SparkContext._do_init
58
106
 
107
+ @ensure_integration_enabled(SparkIntegration, spark_context_init)
59
108
  def _sentry_patched_spark_context_init(self, *args, **kwargs):
60
109
  # type: (SparkContext, *Any, **Any) -> Optional[Any]
61
- init = spark_context_init(self, *args, **kwargs)
62
-
63
- if Hub.current.get_integration(SparkIntegration) is None:
64
- return init
65
-
66
- _start_sentry_listener(self)
67
- _set_app_properties()
110
+ rv = spark_context_init(self, *args, **kwargs)
111
+ _activate_integration(self)
112
+ return rv
68
113
 
69
- with configure_scope() as scope:
70
-
71
- @scope.add_event_processor
72
- def process_event(event, hint):
73
- # type: (Event, Hint) -> Optional[Event]
74
- with capture_internal_exceptions():
75
- if Hub.current.get_integration(SparkIntegration) is None:
76
- return event
77
-
78
- event.setdefault("user", {}).setdefault("id", self.sparkUser())
79
-
80
- event.setdefault("tags", {}).setdefault(
81
- "executor.id", self._conf.get("spark.executor.id")
82
- )
83
- event["tags"].setdefault(
84
- "spark-submit.deployMode",
85
- self._conf.get("spark.submit.deployMode"),
86
- )
87
- event["tags"].setdefault(
88
- "driver.host", self._conf.get("spark.driver.host")
89
- )
90
- event["tags"].setdefault(
91
- "driver.port", self._conf.get("spark.driver.port")
92
- )
93
- event["tags"].setdefault("spark_version", self.version)
94
- event["tags"].setdefault("app_name", self.appName)
95
- event["tags"].setdefault("application_id", self.applicationId)
96
- event["tags"].setdefault("master", self.master)
97
- event["tags"].setdefault("spark_home", self.sparkHome)
98
-
99
- event.setdefault("extra", {}).setdefault("web_url", self.uiWebUrl)
114
+ SparkContext._do_init = _sentry_patched_spark_context_init
100
115
 
101
- return event
102
116
 
103
- return init
117
+ def _setup_sentry_tracing():
118
+ # type: () -> None
119
+ from pyspark import SparkContext
104
120
 
105
- SparkContext._do_init = _sentry_patched_spark_context_init
121
+ if SparkContext._active_spark_context is not None:
122
+ _activate_integration(SparkContext._active_spark_context)
123
+ return
124
+ _patch_spark_context_init()
106
125
 
107
126
 
108
- class SparkListener(object):
127
+ class SparkListener:
109
128
  def onApplicationEnd(self, applicationEnd): # noqa: N802,N803
110
129
  # type: (Any) -> None
111
130
  pass
@@ -139,7 +158,8 @@ class SparkListener(object):
139
158
  pass
140
159
 
141
160
  def onExecutorBlacklistedForStage( # noqa: N802
142
- self, executorBlacklistedForStage # noqa: N803
161
+ self,
162
+ executorBlacklistedForStage, # noqa: N803
143
163
  ):
144
164
  # type: (Any) -> None
145
165
  pass
@@ -209,14 +229,23 @@ class SparkListener(object):
209
229
 
210
230
 
211
231
  class SentryListener(SparkListener):
212
- def __init__(self):
213
- # type: () -> None
214
- self.hub = Hub.current
232
+ def _add_breadcrumb(
233
+ self,
234
+ level, # type: str
235
+ message, # type: str
236
+ data=None, # type: Optional[dict[str, Any]]
237
+ ):
238
+ # type: (...) -> None
239
+ sentry_sdk.get_isolation_scope().add_breadcrumb(
240
+ level=level, message=message, data=data
241
+ )
215
242
 
216
243
  def onJobStart(self, jobStart): # noqa: N802,N803
217
244
  # type: (Any) -> None
245
+ sentry_sdk.get_isolation_scope().clear_breadcrumbs()
246
+
218
247
  message = "Job {} Started".format(jobStart.jobId())
219
- self.hub.add_breadcrumb(level="info", message=message)
248
+ self._add_breadcrumb(level="info", message=message)
220
249
  _set_app_properties()
221
250
 
222
251
  def onJobEnd(self, jobEnd): # noqa: N802,N803
@@ -232,14 +261,19 @@ class SentryListener(SparkListener):
232
261
  level = "warning"
233
262
  message = "Job {} Failed".format(jobEnd.jobId())
234
263
 
235
- self.hub.add_breadcrumb(level=level, message=message, data=data)
264
+ self._add_breadcrumb(level=level, message=message, data=data)
236
265
 
237
266
  def onStageSubmitted(self, stageSubmitted): # noqa: N802,N803
238
267
  # type: (Any) -> None
239
268
  stage_info = stageSubmitted.stageInfo()
240
269
  message = "Stage {} Submitted".format(stage_info.stageId())
241
- data = {"attemptId": stage_info.attemptId(), "name": stage_info.name()}
242
- self.hub.add_breadcrumb(level="info", message=message, data=data)
270
+
271
+ data = {"name": stage_info.name()}
272
+ attempt_id = _get_attempt_id(stage_info)
273
+ if attempt_id is not None:
274
+ data["attemptId"] = attempt_id
275
+
276
+ self._add_breadcrumb(level="info", message=message, data=data)
243
277
  _set_app_properties()
244
278
 
245
279
  def onStageCompleted(self, stageCompleted): # noqa: N802,N803
@@ -249,7 +283,11 @@ class SentryListener(SparkListener):
249
283
  stage_info = stageCompleted.stageInfo()
250
284
  message = ""
251
285
  level = ""
252
- data = {"attemptId": stage_info.attemptId(), "name": stage_info.name()}
286
+
287
+ data = {"name": stage_info.name()}
288
+ attempt_id = _get_attempt_id(stage_info)
289
+ if attempt_id is not None:
290
+ data["attemptId"] = attempt_id
253
291
 
254
292
  # Have to Try Except because stageInfo.failureReason() is typed with Scala Option
255
293
  try:
@@ -260,4 +298,19 @@ class SentryListener(SparkListener):
260
298
  message = "Stage {} Completed".format(stage_info.stageId())
261
299
  level = "info"
262
300
 
263
- self.hub.add_breadcrumb(level=level, message=message, data=data)
301
+ self._add_breadcrumb(level=level, message=message, data=data)
302
+
303
+
304
+ def _get_attempt_id(stage_info):
305
+ # type: (Any) -> Optional[int]
306
+ try:
307
+ return stage_info.attemptId()
308
+ except Exception:
309
+ pass
310
+
311
+ try:
312
+ return stage_info.attemptNumber()
313
+ except Exception:
314
+ pass
315
+
316
+ return None
@@ -1,9 +1,6 @@
1
- from __future__ import absolute_import
2
-
3
1
  import sys
4
2
 
5
- from sentry_sdk import configure_scope
6
- from sentry_sdk.hub import Hub
3
+ import sentry_sdk
7
4
  from sentry_sdk.integrations import Integration
8
5
  from sentry_sdk.utils import (
9
6
  capture_internal_exceptions,
@@ -13,9 +10,9 @@ from sentry_sdk.utils import (
13
10
  event_hint_with_exc_info,
14
11
  )
15
12
 
16
- from sentry_sdk._types import MYPY
13
+ from typing import TYPE_CHECKING
17
14
 
18
- if MYPY:
15
+ if TYPE_CHECKING:
19
16
  from typing import Any
20
17
  from typing import Optional
21
18
 
@@ -33,11 +30,9 @@ class SparkWorkerIntegration(Integration):
33
30
  original_daemon.worker_main = _sentry_worker_main
34
31
 
35
32
 
36
- def _capture_exception(exc_info, hub):
37
- # type: (ExcInfo, Hub) -> None
38
- client = hub.client
39
-
40
- client_options = client.options # type: ignore
33
+ def _capture_exception(exc_info):
34
+ # type: (ExcInfo) -> None
35
+ client = sentry_sdk.get_client()
41
36
 
42
37
  mechanism = {"type": "spark", "handled": False}
43
38
 
@@ -51,63 +46,61 @@ def _capture_exception(exc_info, hub):
51
46
  if exc_type not in (SystemExit, EOFError, ConnectionResetError):
52
47
  rv.append(
53
48
  single_exception_from_error_tuple(
54
- exc_type, exc_value, tb, client_options, mechanism
49
+ exc_type, exc_value, tb, client.options, mechanism
55
50
  )
56
51
  )
57
52
 
58
53
  if rv:
59
54
  rv.reverse()
60
55
  hint = event_hint_with_exc_info(exc_info)
61
- event = {"level": "error", "exception": {"values": rv}}
56
+ event = {"level": "error", "exception": {"values": rv}} # type: Event
62
57
 
63
58
  _tag_task_context()
64
59
 
65
- hub.capture_event(event, hint=hint)
60
+ sentry_sdk.capture_event(event, hint=hint)
66
61
 
67
62
 
68
63
  def _tag_task_context():
69
64
  # type: () -> None
70
65
  from pyspark.taskcontext import TaskContext
71
66
 
72
- with configure_scope() as scope:
67
+ scope = sentry_sdk.get_isolation_scope()
73
68
 
74
- @scope.add_event_processor
75
- def process_event(event, hint):
76
- # type: (Event, Hint) -> Optional[Event]
77
- with capture_internal_exceptions():
78
- integration = Hub.current.get_integration(SparkWorkerIntegration)
79
- task_context = TaskContext.get()
69
+ @scope.add_event_processor
70
+ def process_event(event, hint):
71
+ # type: (Event, Hint) -> Optional[Event]
72
+ with capture_internal_exceptions():
73
+ integration = sentry_sdk.get_client().get_integration(
74
+ SparkWorkerIntegration
75
+ )
76
+ task_context = TaskContext.get()
80
77
 
81
- if integration is None or task_context is None:
82
- return event
78
+ if integration is None or task_context is None:
79
+ return event
83
80
 
84
- event.setdefault("tags", {}).setdefault(
85
- "stageId", str(task_context.stageId())
86
- )
87
- event["tags"].setdefault("partitionId", str(task_context.partitionId()))
88
- event["tags"].setdefault(
89
- "attemptNumber", str(task_context.attemptNumber())
90
- )
91
- event["tags"].setdefault(
92
- "taskAttemptId", str(task_context.taskAttemptId())
93
- )
81
+ event.setdefault("tags", {}).setdefault(
82
+ "stageId", str(task_context.stageId())
83
+ )
84
+ event["tags"].setdefault("partitionId", str(task_context.partitionId()))
85
+ event["tags"].setdefault("attemptNumber", str(task_context.attemptNumber()))
86
+ event["tags"].setdefault("taskAttemptId", str(task_context.taskAttemptId()))
94
87
 
95
- if task_context._localProperties:
96
- if "sentry_app_name" in task_context._localProperties:
97
- event["tags"].setdefault(
98
- "app_name", task_context._localProperties["sentry_app_name"]
99
- )
100
- event["tags"].setdefault(
101
- "application_id",
102
- task_context._localProperties["sentry_application_id"],
103
- )
88
+ if task_context._localProperties:
89
+ if "sentry_app_name" in task_context._localProperties:
90
+ event["tags"].setdefault(
91
+ "app_name", task_context._localProperties["sentry_app_name"]
92
+ )
93
+ event["tags"].setdefault(
94
+ "application_id",
95
+ task_context._localProperties["sentry_application_id"],
96
+ )
104
97
 
105
- if "callSite.short" in task_context._localProperties:
106
- event.setdefault("extra", {}).setdefault(
107
- "callSite", task_context._localProperties["callSite.short"]
108
- )
98
+ if "callSite.short" in task_context._localProperties:
99
+ event.setdefault("extra", {}).setdefault(
100
+ "callSite", task_context._localProperties["callSite.short"]
101
+ )
109
102
 
110
- return event
103
+ return event
111
104
 
112
105
 
113
106
  def _sentry_worker_main(*args, **kwargs):
@@ -117,8 +110,7 @@ def _sentry_worker_main(*args, **kwargs):
117
110
  try:
118
111
  original_worker.main(*args, **kwargs)
119
112
  except SystemExit:
120
- if Hub.current.get_integration(SparkWorkerIntegration) is not None:
121
- hub = Hub.current
113
+ if sentry_sdk.get_client().get_integration(SparkWorkerIntegration) is not None:
122
114
  exc_info = sys.exc_info()
123
115
  with capture_internal_exceptions():
124
- _capture_exception(exc_info, hub)
116
+ _capture_exception(exc_info)
@@ -1,9 +1,11 @@
1
- from __future__ import absolute_import
2
-
3
- from sentry_sdk._types import MYPY
4
- from sentry_sdk.hub import Hub
5
- from sentry_sdk.integrations import Integration, DidNotEnable
6
- from sentry_sdk.tracing import record_sql_queries
1
+ from sentry_sdk.consts import SPANSTATUS, SPANDATA
2
+ from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
3
+ from sentry_sdk.tracing_utils import add_query_source, record_sql_queries
4
+ from sentry_sdk.utils import (
5
+ capture_internal_exceptions,
6
+ ensure_integration_enabled,
7
+ parse_version,
8
+ )
7
9
 
8
10
  try:
9
11
  from sqlalchemy.engine import Engine # type: ignore
@@ -12,7 +14,9 @@ try:
12
14
  except ImportError:
13
15
  raise DidNotEnable("SQLAlchemy not installed.")
14
16
 
15
- if MYPY:
17
+ from typing import TYPE_CHECKING
18
+
19
+ if TYPE_CHECKING:
16
20
  from typing import Any
17
21
  from typing import ContextManager
18
22
  from typing import Optional
@@ -22,76 +26,117 @@ if MYPY:
22
26
 
23
27
  class SqlalchemyIntegration(Integration):
24
28
  identifier = "sqlalchemy"
29
+ origin = f"auto.db.{identifier}"
25
30
 
26
31
  @staticmethod
27
32
  def setup_once():
28
33
  # type: () -> None
29
-
30
- try:
31
- version = tuple(map(int, SQLALCHEMY_VERSION.split("b")[0].split(".")))
32
- except (TypeError, ValueError):
33
- raise DidNotEnable(
34
- "Unparsable SQLAlchemy version: {}".format(SQLALCHEMY_VERSION)
35
- )
36
-
37
- if version < (1, 2):
38
- raise DidNotEnable("SQLAlchemy 1.2 or newer required.")
34
+ version = parse_version(SQLALCHEMY_VERSION)
35
+ _check_minimum_version(SqlalchemyIntegration, version)
39
36
 
40
37
  listen(Engine, "before_cursor_execute", _before_cursor_execute)
41
38
  listen(Engine, "after_cursor_execute", _after_cursor_execute)
42
39
  listen(Engine, "handle_error", _handle_error)
43
40
 
44
41
 
42
+ @ensure_integration_enabled(SqlalchemyIntegration)
45
43
  def _before_cursor_execute(
46
44
  conn, cursor, statement, parameters, context, executemany, *args
47
45
  ):
48
46
  # type: (Any, Any, Any, Any, Any, bool, *Any) -> None
49
- hub = Hub.current
50
- if hub.get_integration(SqlalchemyIntegration) is None:
51
- return
52
-
53
47
  ctx_mgr = record_sql_queries(
54
- hub,
55
48
  cursor,
56
49
  statement,
57
50
  parameters,
58
51
  paramstyle=context and context.dialect and context.dialect.paramstyle or None,
59
52
  executemany=executemany,
53
+ span_origin=SqlalchemyIntegration.origin,
60
54
  )
61
- conn._sentry_sql_span_manager = ctx_mgr
55
+ context._sentry_sql_span_manager = ctx_mgr
62
56
 
63
57
  span = ctx_mgr.__enter__()
64
58
 
65
59
  if span is not None:
66
- conn._sentry_sql_span = span
60
+ _set_db_data(span, conn)
61
+ context._sentry_sql_span = span
67
62
 
68
63
 
69
- def _after_cursor_execute(conn, cursor, statement, *args):
70
- # type: (Any, Any, Any, *Any) -> None
71
- ctx_mgr = getattr(
72
- conn, "_sentry_sql_span_manager", None
73
- ) # type: ContextManager[Any]
64
+ @ensure_integration_enabled(SqlalchemyIntegration)
65
+ def _after_cursor_execute(conn, cursor, statement, parameters, context, *args):
66
+ # type: (Any, Any, Any, Any, Any, *Any) -> None
67
+ ctx_mgr = getattr(context, "_sentry_sql_span_manager", None) # type: Optional[ContextManager[Any]]
74
68
 
75
69
  if ctx_mgr is not None:
76
- conn._sentry_sql_span_manager = None
70
+ context._sentry_sql_span_manager = None
77
71
  ctx_mgr.__exit__(None, None, None)
78
72
 
73
+ span = getattr(context, "_sentry_sql_span", None) # type: Optional[Span]
74
+ if span is not None:
75
+ with capture_internal_exceptions():
76
+ add_query_source(span)
77
+
79
78
 
80
79
  def _handle_error(context, *args):
81
80
  # type: (Any, *Any) -> None
82
- conn = context.connection
83
- span = getattr(conn, "_sentry_sql_span", None) # type: Optional[Span]
81
+ execution_context = context.execution_context
82
+ if execution_context is None:
83
+ return
84
+
85
+ span = getattr(execution_context, "_sentry_sql_span", None) # type: Optional[Span]
84
86
 
85
87
  if span is not None:
86
- span.set_status("internal_error")
88
+ span.set_status(SPANSTATUS.INTERNAL_ERROR)
87
89
 
88
90
  # _after_cursor_execute does not get called for crashing SQL stmts. Judging
89
91
  # from SQLAlchemy codebase it does seem like any error coming into this
90
92
  # handler is going to be fatal.
91
- ctx_mgr = getattr(
92
- conn, "_sentry_sql_span_manager", None
93
- ) # type: ContextManager[Any]
93
+ ctx_mgr = getattr(execution_context, "_sentry_sql_span_manager", None) # type: Optional[ContextManager[Any]]
94
94
 
95
95
  if ctx_mgr is not None:
96
- conn._sentry_sql_span_manager = None
96
+ execution_context._sentry_sql_span_manager = None
97
97
  ctx_mgr.__exit__(None, None, None)
98
+
99
+
100
+ # See: https://docs.sqlalchemy.org/en/20/dialects/index.html
101
+ def _get_db_system(name):
102
+ # type: (str) -> Optional[str]
103
+ name = str(name)
104
+
105
+ if "sqlite" in name:
106
+ return "sqlite"
107
+
108
+ if "postgres" in name:
109
+ return "postgresql"
110
+
111
+ if "mariadb" in name:
112
+ return "mariadb"
113
+
114
+ if "mysql" in name:
115
+ return "mysql"
116
+
117
+ if "oracle" in name:
118
+ return "oracle"
119
+
120
+ return None
121
+
122
+
123
+ def _set_db_data(span, conn):
124
+ # type: (Span, Any) -> None
125
+ db_system = _get_db_system(conn.engine.name)
126
+ if db_system is not None:
127
+ span.set_data(SPANDATA.DB_SYSTEM, db_system)
128
+
129
+ if conn.engine.url is None:
130
+ return
131
+
132
+ db_name = conn.engine.url.database
133
+ if db_name is not None:
134
+ span.set_data(SPANDATA.DB_NAME, db_name)
135
+
136
+ server_address = conn.engine.url.host
137
+ if server_address is not None:
138
+ span.set_data(SPANDATA.SERVER_ADDRESS, server_address)
139
+
140
+ server_port = conn.engine.url.port
141
+ if server_port is not None:
142
+ span.set_data(SPANDATA.SERVER_PORT, server_port)