aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/sentry_sdk/integrations/sqlalchemy.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/sentry_sdk/integrations/sqlalchemy.py')
-rw-r--r--.venv/lib/python3.12/site-packages/sentry_sdk/integrations/sqlalchemy.py146
1 files changed, 146 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/integrations/sqlalchemy.py b/.venv/lib/python3.12/site-packages/sentry_sdk/integrations/sqlalchemy.py
new file mode 100644
index 00000000..068d3730
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sentry_sdk/integrations/sqlalchemy.py
@@ -0,0 +1,146 @@
+from sentry_sdk.consts import SPANSTATUS, SPANDATA
+from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
+from sentry_sdk.tracing_utils import add_query_source, record_sql_queries
+from sentry_sdk.utils import (
+ capture_internal_exceptions,
+ ensure_integration_enabled,
+ parse_version,
+)
+
+try:
+ from sqlalchemy.engine import Engine # type: ignore
+ from sqlalchemy.event import listen # type: ignore
+ from sqlalchemy import __version__ as SQLALCHEMY_VERSION # type: ignore
+except ImportError:
+ raise DidNotEnable("SQLAlchemy not installed.")
+
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from typing import Any
+ from typing import ContextManager
+ from typing import Optional
+
+ from sentry_sdk.tracing import Span
+
+
+class SqlalchemyIntegration(Integration):
+ identifier = "sqlalchemy"
+ origin = f"auto.db.{identifier}"
+
+ @staticmethod
+ def setup_once():
+ # type: () -> None
+ version = parse_version(SQLALCHEMY_VERSION)
+ _check_minimum_version(SqlalchemyIntegration, version)
+
+ listen(Engine, "before_cursor_execute", _before_cursor_execute)
+ listen(Engine, "after_cursor_execute", _after_cursor_execute)
+ listen(Engine, "handle_error", _handle_error)
+
+
+@ensure_integration_enabled(SqlalchemyIntegration)
+def _before_cursor_execute(
+ conn, cursor, statement, parameters, context, executemany, *args
+):
+ # type: (Any, Any, Any, Any, Any, bool, *Any) -> None
+ ctx_mgr = record_sql_queries(
+ cursor,
+ statement,
+ parameters,
+ paramstyle=context and context.dialect and context.dialect.paramstyle or None,
+ executemany=executemany,
+ span_origin=SqlalchemyIntegration.origin,
+ )
+ context._sentry_sql_span_manager = ctx_mgr
+
+ span = ctx_mgr.__enter__()
+
+ if span is not None:
+ _set_db_data(span, conn)
+ context._sentry_sql_span = span
+
+
+@ensure_integration_enabled(SqlalchemyIntegration)
+def _after_cursor_execute(conn, cursor, statement, parameters, context, *args):
+ # type: (Any, Any, Any, Any, Any, *Any) -> None
+ ctx_mgr = getattr(
+ context, "_sentry_sql_span_manager", None
+ ) # type: Optional[ContextManager[Any]]
+
+ if ctx_mgr is not None:
+ context._sentry_sql_span_manager = None
+ ctx_mgr.__exit__(None, None, None)
+
+ span = getattr(context, "_sentry_sql_span", None) # type: Optional[Span]
+ if span is not None:
+ with capture_internal_exceptions():
+ add_query_source(span)
+
+
+def _handle_error(context, *args):
+ # type: (Any, *Any) -> None
+ execution_context = context.execution_context
+ if execution_context is None:
+ return
+
+ span = getattr(execution_context, "_sentry_sql_span", None) # type: Optional[Span]
+
+ if span is not None:
+ span.set_status(SPANSTATUS.INTERNAL_ERROR)
+
+ # _after_cursor_execute does not get called for crashing SQL stmts. Judging
+ # from SQLAlchemy codebase it does seem like any error coming into this
+ # handler is going to be fatal.
+ ctx_mgr = getattr(
+ execution_context, "_sentry_sql_span_manager", None
+ ) # type: Optional[ContextManager[Any]]
+
+ if ctx_mgr is not None:
+ execution_context._sentry_sql_span_manager = None
+ ctx_mgr.__exit__(None, None, None)
+
+
+# See: https://docs.sqlalchemy.org/en/20/dialects/index.html
+def _get_db_system(name):
+ # type: (str) -> Optional[str]
+ name = str(name)
+
+ if "sqlite" in name:
+ return "sqlite"
+
+ if "postgres" in name:
+ return "postgresql"
+
+ if "mariadb" in name:
+ return "mariadb"
+
+ if "mysql" in name:
+ return "mysql"
+
+ if "oracle" in name:
+ return "oracle"
+
+ return None
+
+
+def _set_db_data(span, conn):
+ # type: (Span, Any) -> None
+ db_system = _get_db_system(conn.engine.name)
+ if db_system is not None:
+ span.set_data(SPANDATA.DB_SYSTEM, db_system)
+
+ if conn.engine.url is None:
+ return
+
+ db_name = conn.engine.url.database
+ if db_name is not None:
+ span.set_data(SPANDATA.DB_NAME, db_name)
+
+ server_address = conn.engine.url.host
+ if server_address is not None:
+ span.set_data(SPANDATA.SERVER_ADDRESS, server_address)
+
+ server_port = conn.engine.url.port
+ if server_port is not None:
+ span.set_data(SPANDATA.SERVER_PORT, server_port)