fix: flatten React Router structure — no nested Routes, each page is a top-level route

This commit is contained in:
enzo
2026-07-22 19:21:29 +08:00
parent 07ff8c2a12
commit 1de5fcf698
3720 changed files with 1254827 additions and 49 deletions
@@ -0,0 +1,4 @@
from sentry_sdk.integrations.spark.spark_driver import SparkIntegration
from sentry_sdk.integrations.spark.spark_worker import SparkWorkerIntegration
__all__ = ["SparkIntegration", "SparkWorkerIntegration"]
@@ -0,0 +1,278 @@
from typing import TYPE_CHECKING
import sentry_sdk
from sentry_sdk.integrations import Integration
from sentry_sdk.utils import capture_internal_exceptions, ensure_integration_enabled
if TYPE_CHECKING:
from typing import Any, Optional
from pyspark import SparkContext
from sentry_sdk._types import Event, Hint
class SparkIntegration(Integration):
identifier = "spark"
@staticmethod
def setup_once() -> None:
_setup_sentry_tracing()
def _set_app_properties() -> None:
"""
Set properties in driver that propagate to worker processes, allowing for workers to have access to those properties.
This allows worker integration to have access to app_name and application_id.
"""
from pyspark import SparkContext
spark_context = SparkContext._active_spark_context
if spark_context:
spark_context.setLocalProperty(
"sentry_app_name",
spark_context.appName,
)
spark_context.setLocalProperty(
"sentry_application_id",
spark_context.applicationId,
)
def _start_sentry_listener(sc: "SparkContext") -> None:
"""
Start java gateway server to add custom `SparkListener`
"""
from pyspark.java_gateway import ensure_callback_server_started
gw = sc._gateway
ensure_callback_server_started(gw)
listener = SentryListener()
sc._jsc.sc().addSparkListener(listener)
def _add_event_processor(sc: "SparkContext") -> None:
scope = sentry_sdk.get_isolation_scope()
@scope.add_event_processor
def process_event(event: "Event", hint: "Hint") -> "Optional[Event]":
with capture_internal_exceptions():
if sentry_sdk.get_client().get_integration(SparkIntegration) is None:
return event
if sc._active_spark_context is None:
return event
event.setdefault("user", {}).setdefault("id", sc.sparkUser())
event.setdefault("tags", {}).setdefault(
"executor.id", sc._conf.get("spark.executor.id")
)
event["tags"].setdefault(
"spark-submit.deployMode",
sc._conf.get("spark.submit.deployMode"),
)
event["tags"].setdefault("driver.host", sc._conf.get("spark.driver.host"))
event["tags"].setdefault("driver.port", sc._conf.get("spark.driver.port"))
event["tags"].setdefault("spark_version", sc.version)
event["tags"].setdefault("app_name", sc.appName)
event["tags"].setdefault("application_id", sc.applicationId)
event["tags"].setdefault("master", sc.master)
event["tags"].setdefault("spark_home", sc.sparkHome)
event.setdefault("extra", {}).setdefault("web_url", sc.uiWebUrl)
return event
def _activate_integration(sc: "SparkContext") -> None:
_start_sentry_listener(sc)
_set_app_properties()
_add_event_processor(sc)
def _patch_spark_context_init() -> None:
from pyspark import SparkContext
spark_context_init = SparkContext._do_init
@ensure_integration_enabled(SparkIntegration, spark_context_init)
def _sentry_patched_spark_context_init(
self: "SparkContext", *args: "Any", **kwargs: "Any"
) -> "Optional[Any]":
rv = spark_context_init(self, *args, **kwargs)
_activate_integration(self)
return rv
SparkContext._do_init = _sentry_patched_spark_context_init
def _setup_sentry_tracing() -> None:
from pyspark import SparkContext
if SparkContext._active_spark_context is not None:
_activate_integration(SparkContext._active_spark_context)
return
_patch_spark_context_init()
class SparkListener:
def onApplicationEnd(self, applicationEnd: "Any") -> None: # noqa: N802,N803
pass
def onApplicationStart(self, applicationStart: "Any") -> None: # noqa: N802,N803
pass
def onBlockManagerAdded(self, blockManagerAdded: "Any") -> None: # noqa: N802,N803
pass
def onBlockManagerRemoved(self, blockManagerRemoved: "Any") -> None: # noqa: N802,N803
pass
def onBlockUpdated(self, blockUpdated: "Any") -> None: # noqa: N802,N803
pass
def onEnvironmentUpdate(self, environmentUpdate: "Any") -> None: # noqa: N802,N803
pass
def onExecutorAdded(self, executorAdded: "Any") -> None: # noqa: N802,N803
pass
def onExecutorBlacklisted(self, executorBlacklisted: "Any") -> None: # noqa: N802,N803
pass
def onExecutorBlacklistedForStage( # noqa: N802
self,
executorBlacklistedForStage: "Any", # noqa: N803
) -> None:
pass
def onExecutorMetricsUpdate(self, executorMetricsUpdate: "Any") -> None: # noqa: N802,N803
pass
def onExecutorRemoved(self, executorRemoved: "Any") -> None: # noqa: N802,N803
pass
def onJobEnd(self, jobEnd: "Any") -> None: # noqa: N802,N803
pass
def onJobStart(self, jobStart: "Any") -> None: # noqa: N802,N803
pass
def onNodeBlacklisted(self, nodeBlacklisted: "Any") -> None: # noqa: N802,N803
pass
def onNodeBlacklistedForStage(self, nodeBlacklistedForStage: "Any") -> None: # noqa: N802,N803
pass
def onNodeUnblacklisted(self, nodeUnblacklisted: "Any") -> None: # noqa: N802,N803
pass
def onOtherEvent(self, event: "Any") -> None: # noqa: N802,N803
pass
def onSpeculativeTaskSubmitted(self, speculativeTask: "Any") -> None: # noqa: N802,N803
pass
def onStageCompleted(self, stageCompleted: "Any") -> None: # noqa: N802,N803
pass
def onStageSubmitted(self, stageSubmitted: "Any") -> None: # noqa: N802,N803
pass
def onTaskEnd(self, taskEnd: "Any") -> None: # noqa: N802,N803
pass
def onTaskGettingResult(self, taskGettingResult: "Any") -> None: # noqa: N802,N803
pass
def onTaskStart(self, taskStart: "Any") -> None: # noqa: N802,N803
pass
def onUnpersistRDD(self, unpersistRDD: "Any") -> None: # noqa: N802,N803
pass
class Java:
implements = ["org.apache.spark.scheduler.SparkListenerInterface"]
class SentryListener(SparkListener):
def _add_breadcrumb(
self,
level: str,
message: str,
data: "Optional[dict[str, Any]]" = None,
) -> None:
sentry_sdk.get_isolation_scope().add_breadcrumb(
level=level, message=message, data=data
)
def onJobStart(self, jobStart: "Any") -> None: # noqa: N802,N803
sentry_sdk.get_isolation_scope().clear_breadcrumbs()
message = "Job {} Started".format(jobStart.jobId())
self._add_breadcrumb(level="info", message=message)
_set_app_properties()
def onJobEnd(self, jobEnd: "Any") -> None: # noqa: N802,N803
level = ""
message = ""
data = {"result": jobEnd.jobResult().toString()}
if jobEnd.jobResult().toString() == "JobSucceeded":
level = "info"
message = "Job {} Ended".format(jobEnd.jobId())
else:
level = "warning"
message = "Job {} Failed".format(jobEnd.jobId())
self._add_breadcrumb(level=level, message=message, data=data)
def onStageSubmitted(self, stageSubmitted: "Any") -> None: # noqa: N802,N803
stage_info = stageSubmitted.stageInfo()
message = "Stage {} Submitted".format(stage_info.stageId())
data = {"name": stage_info.name()}
attempt_id = _get_attempt_id(stage_info)
if attempt_id is not None:
data["attemptId"] = attempt_id
self._add_breadcrumb(level="info", message=message, data=data)
_set_app_properties()
def onStageCompleted(self, stageCompleted: "Any") -> None: # noqa: N802,N803
from py4j.protocol import Py4JJavaError # type: ignore
stage_info = stageCompleted.stageInfo()
message = ""
level = ""
data = {"name": stage_info.name()}
attempt_id = _get_attempt_id(stage_info)
if attempt_id is not None:
data["attemptId"] = attempt_id
# Have to Try Except because stageInfo.failureReason() is typed with Scala Option
try:
data["reason"] = stage_info.failureReason().get()
message = "Stage {} Failed".format(stage_info.stageId())
level = "warning"
except Py4JJavaError:
message = "Stage {} Completed".format(stage_info.stageId())
level = "info"
self._add_breadcrumb(level=level, message=message, data=data)
def _get_attempt_id(stage_info: "Any") -> "Optional[int]":
try:
return stage_info.attemptId()
except Exception:
pass
try:
return stage_info.attemptNumber()
except Exception:
pass
return None
@@ -0,0 +1,109 @@
import sys
from typing import TYPE_CHECKING
import sentry_sdk
from sentry_sdk.integrations import Integration
from sentry_sdk.utils import (
capture_internal_exceptions,
event_hint_with_exc_info,
exc_info_from_error,
single_exception_from_error_tuple,
walk_exception_chain,
)
if TYPE_CHECKING:
from typing import Any, Optional
from sentry_sdk._types import Event, ExcInfo, Hint
class SparkWorkerIntegration(Integration):
identifier = "spark_worker"
@staticmethod
def setup_once() -> None:
import pyspark.daemon as original_daemon
original_daemon.worker_main = _sentry_worker_main
def _capture_exception(exc_info: "ExcInfo") -> None:
client = sentry_sdk.get_client()
mechanism = {"type": "spark", "handled": False}
exc_info = exc_info_from_error(exc_info)
exc_type, exc_value, tb = exc_info
rv = []
# On Exception worker will call sys.exit(-1), so we can ignore SystemExit and similar errors
for exc_type, exc_value, tb in walk_exception_chain(exc_info):
if exc_type not in (SystemExit, EOFError, ConnectionResetError):
rv.append(
single_exception_from_error_tuple(
exc_type, exc_value, tb, client.options, mechanism
)
)
if rv:
rv.reverse()
hint = event_hint_with_exc_info(exc_info)
event: "Event" = {"level": "error", "exception": {"values": rv}}
_tag_task_context()
sentry_sdk.capture_event(event, hint=hint)
def _tag_task_context() -> None:
from pyspark.taskcontext import TaskContext
scope = sentry_sdk.get_isolation_scope()
@scope.add_event_processor
def process_event(event: "Event", hint: "Hint") -> "Optional[Event]":
with capture_internal_exceptions():
integration = sentry_sdk.get_client().get_integration(
SparkWorkerIntegration
)
task_context = TaskContext.get()
if integration is None or task_context is None:
return event
event.setdefault("tags", {}).setdefault(
"stageId", str(task_context.stageId())
)
event["tags"].setdefault("partitionId", str(task_context.partitionId()))
event["tags"].setdefault("attemptNumber", str(task_context.attemptNumber()))
event["tags"].setdefault("taskAttemptId", str(task_context.taskAttemptId()))
if task_context._localProperties:
if "sentry_app_name" in task_context._localProperties:
event["tags"].setdefault(
"app_name", task_context._localProperties["sentry_app_name"]
)
event["tags"].setdefault(
"application_id",
task_context._localProperties["sentry_application_id"],
)
if "callSite.short" in task_context._localProperties:
event.setdefault("extra", {}).setdefault(
"callSite", task_context._localProperties["callSite.short"]
)
return event
def _sentry_worker_main(*args: "Optional[Any]", **kwargs: "Optional[Any]") -> None:
import pyspark.worker as original_worker
try:
original_worker.main(*args, **kwargs)
except SystemExit:
if sentry_sdk.get_client().get_integration(SparkWorkerIntegration) is not None:
exc_info = sys.exc_info()
with capture_internal_exceptions():
_capture_exception(exc_info)