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.
- sentry_sdk/__init__.py +48 -6
- sentry_sdk/_compat.py +64 -56
- sentry_sdk/_init_implementation.py +84 -0
- sentry_sdk/_log_batcher.py +172 -0
- sentry_sdk/_lru_cache.py +47 -0
- sentry_sdk/_metrics_batcher.py +167 -0
- sentry_sdk/_queue.py +81 -19
- sentry_sdk/_types.py +311 -11
- sentry_sdk/_werkzeug.py +98 -0
- sentry_sdk/ai/__init__.py +7 -0
- sentry_sdk/ai/monitoring.py +137 -0
- sentry_sdk/ai/utils.py +144 -0
- sentry_sdk/api.py +409 -67
- sentry_sdk/attachments.py +75 -0
- sentry_sdk/client.py +849 -103
- sentry_sdk/consts.py +1389 -34
- sentry_sdk/crons/__init__.py +10 -0
- sentry_sdk/crons/api.py +62 -0
- sentry_sdk/crons/consts.py +4 -0
- sentry_sdk/crons/decorator.py +135 -0
- sentry_sdk/debug.py +12 -15
- sentry_sdk/envelope.py +112 -61
- sentry_sdk/feature_flags.py +71 -0
- sentry_sdk/hub.py +442 -386
- sentry_sdk/integrations/__init__.py +228 -58
- sentry_sdk/integrations/_asgi_common.py +108 -0
- sentry_sdk/integrations/_wsgi_common.py +131 -40
- sentry_sdk/integrations/aiohttp.py +221 -72
- sentry_sdk/integrations/anthropic.py +439 -0
- sentry_sdk/integrations/argv.py +4 -6
- sentry_sdk/integrations/ariadne.py +161 -0
- sentry_sdk/integrations/arq.py +247 -0
- sentry_sdk/integrations/asgi.py +237 -135
- sentry_sdk/integrations/asyncio.py +144 -0
- sentry_sdk/integrations/asyncpg.py +208 -0
- sentry_sdk/integrations/atexit.py +13 -18
- sentry_sdk/integrations/aws_lambda.py +233 -80
- sentry_sdk/integrations/beam.py +27 -35
- sentry_sdk/integrations/boto3.py +137 -0
- sentry_sdk/integrations/bottle.py +91 -69
- sentry_sdk/integrations/celery/__init__.py +529 -0
- sentry_sdk/integrations/celery/beat.py +293 -0
- sentry_sdk/integrations/celery/utils.py +43 -0
- sentry_sdk/integrations/chalice.py +35 -28
- sentry_sdk/integrations/clickhouse_driver.py +177 -0
- sentry_sdk/integrations/cloud_resource_context.py +280 -0
- sentry_sdk/integrations/cohere.py +274 -0
- sentry_sdk/integrations/dedupe.py +32 -8
- sentry_sdk/integrations/django/__init__.py +343 -89
- sentry_sdk/integrations/django/asgi.py +201 -22
- sentry_sdk/integrations/django/caching.py +204 -0
- sentry_sdk/integrations/django/middleware.py +80 -32
- sentry_sdk/integrations/django/signals_handlers.py +91 -0
- sentry_sdk/integrations/django/templates.py +69 -2
- sentry_sdk/integrations/django/transactions.py +39 -14
- sentry_sdk/integrations/django/views.py +69 -16
- sentry_sdk/integrations/dramatiq.py +226 -0
- sentry_sdk/integrations/excepthook.py +19 -13
- sentry_sdk/integrations/executing.py +5 -6
- sentry_sdk/integrations/falcon.py +128 -65
- sentry_sdk/integrations/fastapi.py +141 -0
- sentry_sdk/integrations/flask.py +114 -75
- sentry_sdk/integrations/gcp.py +67 -36
- sentry_sdk/integrations/gnu_backtrace.py +14 -22
- sentry_sdk/integrations/google_genai/__init__.py +301 -0
- sentry_sdk/integrations/google_genai/consts.py +16 -0
- sentry_sdk/integrations/google_genai/streaming.py +155 -0
- sentry_sdk/integrations/google_genai/utils.py +576 -0
- sentry_sdk/integrations/gql.py +162 -0
- sentry_sdk/integrations/graphene.py +151 -0
- sentry_sdk/integrations/grpc/__init__.py +168 -0
- sentry_sdk/integrations/grpc/aio/__init__.py +7 -0
- sentry_sdk/integrations/grpc/aio/client.py +95 -0
- sentry_sdk/integrations/grpc/aio/server.py +100 -0
- sentry_sdk/integrations/grpc/client.py +91 -0
- sentry_sdk/integrations/grpc/consts.py +1 -0
- sentry_sdk/integrations/grpc/server.py +66 -0
- sentry_sdk/integrations/httpx.py +178 -0
- sentry_sdk/integrations/huey.py +174 -0
- sentry_sdk/integrations/huggingface_hub.py +378 -0
- sentry_sdk/integrations/langchain.py +1132 -0
- sentry_sdk/integrations/langgraph.py +337 -0
- sentry_sdk/integrations/launchdarkly.py +61 -0
- sentry_sdk/integrations/litellm.py +287 -0
- sentry_sdk/integrations/litestar.py +315 -0
- sentry_sdk/integrations/logging.py +261 -85
- sentry_sdk/integrations/loguru.py +213 -0
- sentry_sdk/integrations/mcp.py +566 -0
- sentry_sdk/integrations/modules.py +6 -33
- sentry_sdk/integrations/openai.py +725 -0
- sentry_sdk/integrations/openai_agents/__init__.py +61 -0
- sentry_sdk/integrations/openai_agents/consts.py +1 -0
- sentry_sdk/integrations/openai_agents/patches/__init__.py +5 -0
- sentry_sdk/integrations/openai_agents/patches/agent_run.py +140 -0
- sentry_sdk/integrations/openai_agents/patches/error_tracing.py +77 -0
- sentry_sdk/integrations/openai_agents/patches/models.py +50 -0
- sentry_sdk/integrations/openai_agents/patches/runner.py +45 -0
- sentry_sdk/integrations/openai_agents/patches/tools.py +77 -0
- sentry_sdk/integrations/openai_agents/spans/__init__.py +5 -0
- sentry_sdk/integrations/openai_agents/spans/agent_workflow.py +21 -0
- sentry_sdk/integrations/openai_agents/spans/ai_client.py +42 -0
- sentry_sdk/integrations/openai_agents/spans/execute_tool.py +48 -0
- sentry_sdk/integrations/openai_agents/spans/handoff.py +19 -0
- sentry_sdk/integrations/openai_agents/spans/invoke_agent.py +86 -0
- sentry_sdk/integrations/openai_agents/utils.py +199 -0
- sentry_sdk/integrations/openfeature.py +35 -0
- sentry_sdk/integrations/opentelemetry/__init__.py +7 -0
- sentry_sdk/integrations/opentelemetry/consts.py +5 -0
- sentry_sdk/integrations/opentelemetry/integration.py +58 -0
- sentry_sdk/integrations/opentelemetry/propagator.py +117 -0
- sentry_sdk/integrations/opentelemetry/span_processor.py +391 -0
- sentry_sdk/integrations/otlp.py +82 -0
- sentry_sdk/integrations/pure_eval.py +20 -11
- sentry_sdk/integrations/pydantic_ai/__init__.py +47 -0
- sentry_sdk/integrations/pydantic_ai/consts.py +1 -0
- sentry_sdk/integrations/pydantic_ai/patches/__init__.py +4 -0
- sentry_sdk/integrations/pydantic_ai/patches/agent_run.py +215 -0
- sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py +110 -0
- sentry_sdk/integrations/pydantic_ai/patches/model_request.py +40 -0
- sentry_sdk/integrations/pydantic_ai/patches/tools.py +98 -0
- sentry_sdk/integrations/pydantic_ai/spans/__init__.py +3 -0
- sentry_sdk/integrations/pydantic_ai/spans/ai_client.py +246 -0
- sentry_sdk/integrations/pydantic_ai/spans/execute_tool.py +49 -0
- sentry_sdk/integrations/pydantic_ai/spans/invoke_agent.py +112 -0
- sentry_sdk/integrations/pydantic_ai/utils.py +223 -0
- sentry_sdk/integrations/pymongo.py +214 -0
- sentry_sdk/integrations/pyramid.py +71 -60
- sentry_sdk/integrations/quart.py +237 -0
- sentry_sdk/integrations/ray.py +165 -0
- sentry_sdk/integrations/redis/__init__.py +48 -0
- sentry_sdk/integrations/redis/_async_common.py +116 -0
- sentry_sdk/integrations/redis/_sync_common.py +119 -0
- sentry_sdk/integrations/redis/consts.py +19 -0
- sentry_sdk/integrations/redis/modules/__init__.py +0 -0
- sentry_sdk/integrations/redis/modules/caches.py +118 -0
- sentry_sdk/integrations/redis/modules/queries.py +65 -0
- sentry_sdk/integrations/redis/rb.py +32 -0
- sentry_sdk/integrations/redis/redis.py +69 -0
- sentry_sdk/integrations/redis/redis_cluster.py +107 -0
- sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +50 -0
- sentry_sdk/integrations/redis/utils.py +148 -0
- sentry_sdk/integrations/rq.py +62 -52
- sentry_sdk/integrations/rust_tracing.py +284 -0
- sentry_sdk/integrations/sanic.py +248 -114
- sentry_sdk/integrations/serverless.py +13 -22
- sentry_sdk/integrations/socket.py +96 -0
- sentry_sdk/integrations/spark/spark_driver.py +115 -62
- sentry_sdk/integrations/spark/spark_worker.py +42 -50
- sentry_sdk/integrations/sqlalchemy.py +82 -37
- sentry_sdk/integrations/starlette.py +737 -0
- sentry_sdk/integrations/starlite.py +292 -0
- sentry_sdk/integrations/statsig.py +37 -0
- sentry_sdk/integrations/stdlib.py +100 -58
- sentry_sdk/integrations/strawberry.py +394 -0
- sentry_sdk/integrations/sys_exit.py +70 -0
- sentry_sdk/integrations/threading.py +142 -38
- sentry_sdk/integrations/tornado.py +68 -53
- sentry_sdk/integrations/trytond.py +15 -20
- sentry_sdk/integrations/typer.py +60 -0
- sentry_sdk/integrations/unleash.py +33 -0
- sentry_sdk/integrations/unraisablehook.py +53 -0
- sentry_sdk/integrations/wsgi.py +126 -125
- sentry_sdk/logger.py +96 -0
- sentry_sdk/metrics.py +81 -0
- sentry_sdk/monitor.py +120 -0
- sentry_sdk/profiler/__init__.py +49 -0
- sentry_sdk/profiler/continuous_profiler.py +730 -0
- sentry_sdk/profiler/transaction_profiler.py +839 -0
- sentry_sdk/profiler/utils.py +195 -0
- sentry_sdk/scope.py +1542 -112
- sentry_sdk/scrubber.py +177 -0
- sentry_sdk/serializer.py +152 -210
- sentry_sdk/session.py +177 -0
- sentry_sdk/sessions.py +202 -179
- sentry_sdk/spotlight.py +242 -0
- sentry_sdk/tracing.py +1202 -294
- sentry_sdk/tracing_utils.py +1236 -0
- sentry_sdk/transport.py +693 -189
- sentry_sdk/types.py +52 -0
- sentry_sdk/utils.py +1395 -228
- sentry_sdk/worker.py +30 -17
- sentry_sdk-2.46.0.dist-info/METADATA +268 -0
- sentry_sdk-2.46.0.dist-info/RECORD +189 -0
- {sentry_sdk-0.18.0.dist-info → sentry_sdk-2.46.0.dist-info}/WHEEL +1 -1
- sentry_sdk-2.46.0.dist-info/entry_points.txt +2 -0
- sentry_sdk-2.46.0.dist-info/licenses/LICENSE +21 -0
- sentry_sdk/_functools.py +0 -66
- sentry_sdk/integrations/celery.py +0 -275
- sentry_sdk/integrations/redis.py +0 -103
- sentry_sdk-0.18.0.dist-info/LICENSE +0 -9
- sentry_sdk-0.18.0.dist-info/METADATA +0 -66
- sentry_sdk-0.18.0.dist-info/RECORD +0 -65
- {sentry_sdk-0.18.0.dist-info → sentry_sdk-2.46.0.dist-info}/top_level.txt +0 -0
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
|
|
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
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
7
6
|
|
|
8
|
-
if
|
|
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
|
-
|
|
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
|
-
"
|
|
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: (
|
|
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
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
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
|
-
|
|
117
|
+
def _setup_sentry_tracing():
|
|
118
|
+
# type: () -> None
|
|
119
|
+
from pyspark import SparkContext
|
|
104
120
|
|
|
105
|
-
SparkContext.
|
|
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
|
|
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,
|
|
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
|
|
213
|
-
|
|
214
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
242
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
13
|
+
from typing import TYPE_CHECKING
|
|
17
14
|
|
|
18
|
-
if
|
|
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
|
|
37
|
-
# type: (ExcInfo
|
|
38
|
-
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,
|
|
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
|
-
|
|
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
|
-
|
|
67
|
+
scope = sentry_sdk.get_isolation_scope()
|
|
73
68
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
82
|
-
|
|
78
|
+
if integration is None or task_context is None:
|
|
79
|
+
return event
|
|
83
80
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
116
|
+
_capture_exception(exc_info)
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from sentry_sdk.
|
|
4
|
-
from sentry_sdk.
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
60
|
+
_set_db_data(span, conn)
|
|
61
|
+
context._sentry_sql_span = span
|
|
67
62
|
|
|
68
63
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
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
|
-
|
|
83
|
-
|
|
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(
|
|
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
|
-
|
|
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)
|