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
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
|
|
3
|
+
import sentry_sdk
|
|
4
|
+
from sentry_sdk.consts import OP
|
|
5
|
+
from sentry_sdk.integrations import DidNotEnable, Integration
|
|
6
|
+
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
|
|
7
|
+
from sentry_sdk.scope import should_send_default_pii
|
|
8
|
+
from sentry_sdk.tracing import SOURCE_FOR_STYLE, TransactionSource
|
|
9
|
+
from sentry_sdk.utils import (
|
|
10
|
+
ensure_integration_enabled,
|
|
11
|
+
event_from_exception,
|
|
12
|
+
transaction_from_function,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
from starlite import Request, Starlite, State # type: ignore
|
|
17
|
+
from starlite.handlers.base import BaseRouteHandler # type: ignore
|
|
18
|
+
from starlite.middleware import DefineMiddleware # type: ignore
|
|
19
|
+
from starlite.plugins.base import get_plugin_for_value # type: ignore
|
|
20
|
+
from starlite.routes.http import HTTPRoute # type: ignore
|
|
21
|
+
from starlite.utils import ConnectionDataExtractor, is_async_callable, Ref # type: ignore
|
|
22
|
+
from pydantic import BaseModel # type: ignore
|
|
23
|
+
except ImportError:
|
|
24
|
+
raise DidNotEnable("Starlite is not installed")
|
|
25
|
+
|
|
26
|
+
from typing import TYPE_CHECKING
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from typing import Any, Optional, Union
|
|
30
|
+
from starlite.types import ( # type: ignore
|
|
31
|
+
ASGIApp,
|
|
32
|
+
Hint,
|
|
33
|
+
HTTPReceiveMessage,
|
|
34
|
+
HTTPScope,
|
|
35
|
+
Message,
|
|
36
|
+
Middleware,
|
|
37
|
+
Receive,
|
|
38
|
+
Scope as StarliteScope,
|
|
39
|
+
Send,
|
|
40
|
+
WebSocketReceiveMessage,
|
|
41
|
+
)
|
|
42
|
+
from starlite import MiddlewareProtocol
|
|
43
|
+
from sentry_sdk._types import Event
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
_DEFAULT_TRANSACTION_NAME = "generic Starlite request"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class StarliteIntegration(Integration):
|
|
50
|
+
identifier = "starlite"
|
|
51
|
+
origin = f"auto.http.{identifier}"
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def setup_once():
|
|
55
|
+
# type: () -> None
|
|
56
|
+
patch_app_init()
|
|
57
|
+
patch_middlewares()
|
|
58
|
+
patch_http_route_handle()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class SentryStarliteASGIMiddleware(SentryAsgiMiddleware):
|
|
62
|
+
def __init__(self, app, span_origin=StarliteIntegration.origin):
|
|
63
|
+
# type: (ASGIApp, str) -> None
|
|
64
|
+
super().__init__(
|
|
65
|
+
app=app,
|
|
66
|
+
unsafe_context_data=False,
|
|
67
|
+
transaction_style="endpoint",
|
|
68
|
+
mechanism_type="asgi",
|
|
69
|
+
span_origin=span_origin,
|
|
70
|
+
asgi_version=3,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def patch_app_init():
|
|
75
|
+
# type: () -> None
|
|
76
|
+
"""
|
|
77
|
+
Replaces the Starlite class's `__init__` function in order to inject `after_exception` handlers and set the
|
|
78
|
+
`SentryStarliteASGIMiddleware` as the outmost middleware in the stack.
|
|
79
|
+
See:
|
|
80
|
+
- https://starlite-api.github.io/starlite/usage/0-the-starlite-app/5-application-hooks/#after-exception
|
|
81
|
+
- https://starlite-api.github.io/starlite/usage/7-middleware/0-middleware-intro/
|
|
82
|
+
"""
|
|
83
|
+
old__init__ = Starlite.__init__
|
|
84
|
+
|
|
85
|
+
@ensure_integration_enabled(StarliteIntegration, old__init__)
|
|
86
|
+
def injection_wrapper(self, *args, **kwargs):
|
|
87
|
+
# type: (Starlite, *Any, **Any) -> None
|
|
88
|
+
after_exception = kwargs.pop("after_exception", [])
|
|
89
|
+
kwargs.update(
|
|
90
|
+
after_exception=[
|
|
91
|
+
exception_handler,
|
|
92
|
+
*(
|
|
93
|
+
after_exception
|
|
94
|
+
if isinstance(after_exception, list)
|
|
95
|
+
else [after_exception]
|
|
96
|
+
),
|
|
97
|
+
]
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
middleware = kwargs.get("middleware") or []
|
|
101
|
+
kwargs["middleware"] = [SentryStarliteASGIMiddleware, *middleware]
|
|
102
|
+
old__init__(self, *args, **kwargs)
|
|
103
|
+
|
|
104
|
+
Starlite.__init__ = injection_wrapper
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def patch_middlewares():
|
|
108
|
+
# type: () -> None
|
|
109
|
+
old_resolve_middleware_stack = BaseRouteHandler.resolve_middleware
|
|
110
|
+
|
|
111
|
+
@ensure_integration_enabled(StarliteIntegration, old_resolve_middleware_stack)
|
|
112
|
+
def resolve_middleware_wrapper(self):
|
|
113
|
+
# type: (BaseRouteHandler) -> list[Middleware]
|
|
114
|
+
return [
|
|
115
|
+
enable_span_for_middleware(middleware)
|
|
116
|
+
for middleware in old_resolve_middleware_stack(self)
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
BaseRouteHandler.resolve_middleware = resolve_middleware_wrapper
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def enable_span_for_middleware(middleware):
|
|
123
|
+
# type: (Middleware) -> Middleware
|
|
124
|
+
if (
|
|
125
|
+
not hasattr(middleware, "__call__") # noqa: B004
|
|
126
|
+
or middleware is SentryStarliteASGIMiddleware
|
|
127
|
+
):
|
|
128
|
+
return middleware
|
|
129
|
+
|
|
130
|
+
if isinstance(middleware, DefineMiddleware):
|
|
131
|
+
old_call = middleware.middleware.__call__ # type: ASGIApp
|
|
132
|
+
else:
|
|
133
|
+
old_call = middleware.__call__
|
|
134
|
+
|
|
135
|
+
async def _create_span_call(self, scope, receive, send):
|
|
136
|
+
# type: (MiddlewareProtocol, StarliteScope, Receive, Send) -> None
|
|
137
|
+
if sentry_sdk.get_client().get_integration(StarliteIntegration) is None:
|
|
138
|
+
return await old_call(self, scope, receive, send)
|
|
139
|
+
|
|
140
|
+
middleware_name = self.__class__.__name__
|
|
141
|
+
with sentry_sdk.start_span(
|
|
142
|
+
op=OP.MIDDLEWARE_STARLITE,
|
|
143
|
+
name=middleware_name,
|
|
144
|
+
origin=StarliteIntegration.origin,
|
|
145
|
+
) as middleware_span:
|
|
146
|
+
middleware_span.set_tag("starlite.middleware_name", middleware_name)
|
|
147
|
+
|
|
148
|
+
# Creating spans for the "receive" callback
|
|
149
|
+
async def _sentry_receive(*args, **kwargs):
|
|
150
|
+
# type: (*Any, **Any) -> Union[HTTPReceiveMessage, WebSocketReceiveMessage]
|
|
151
|
+
if sentry_sdk.get_client().get_integration(StarliteIntegration) is None:
|
|
152
|
+
return await receive(*args, **kwargs)
|
|
153
|
+
with sentry_sdk.start_span(
|
|
154
|
+
op=OP.MIDDLEWARE_STARLITE_RECEIVE,
|
|
155
|
+
name=getattr(receive, "__qualname__", str(receive)),
|
|
156
|
+
origin=StarliteIntegration.origin,
|
|
157
|
+
) as span:
|
|
158
|
+
span.set_tag("starlite.middleware_name", middleware_name)
|
|
159
|
+
return await receive(*args, **kwargs)
|
|
160
|
+
|
|
161
|
+
receive_name = getattr(receive, "__name__", str(receive))
|
|
162
|
+
receive_patched = receive_name == "_sentry_receive"
|
|
163
|
+
new_receive = _sentry_receive if not receive_patched else receive
|
|
164
|
+
|
|
165
|
+
# Creating spans for the "send" callback
|
|
166
|
+
async def _sentry_send(message):
|
|
167
|
+
# type: (Message) -> None
|
|
168
|
+
if sentry_sdk.get_client().get_integration(StarliteIntegration) is None:
|
|
169
|
+
return await send(message)
|
|
170
|
+
with sentry_sdk.start_span(
|
|
171
|
+
op=OP.MIDDLEWARE_STARLITE_SEND,
|
|
172
|
+
name=getattr(send, "__qualname__", str(send)),
|
|
173
|
+
origin=StarliteIntegration.origin,
|
|
174
|
+
) as span:
|
|
175
|
+
span.set_tag("starlite.middleware_name", middleware_name)
|
|
176
|
+
return await send(message)
|
|
177
|
+
|
|
178
|
+
send_name = getattr(send, "__name__", str(send))
|
|
179
|
+
send_patched = send_name == "_sentry_send"
|
|
180
|
+
new_send = _sentry_send if not send_patched else send
|
|
181
|
+
|
|
182
|
+
return await old_call(self, scope, new_receive, new_send)
|
|
183
|
+
|
|
184
|
+
not_yet_patched = old_call.__name__ not in ["_create_span_call"]
|
|
185
|
+
|
|
186
|
+
if not_yet_patched:
|
|
187
|
+
if isinstance(middleware, DefineMiddleware):
|
|
188
|
+
middleware.middleware.__call__ = _create_span_call
|
|
189
|
+
else:
|
|
190
|
+
middleware.__call__ = _create_span_call
|
|
191
|
+
|
|
192
|
+
return middleware
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def patch_http_route_handle():
|
|
196
|
+
# type: () -> None
|
|
197
|
+
old_handle = HTTPRoute.handle
|
|
198
|
+
|
|
199
|
+
async def handle_wrapper(self, scope, receive, send):
|
|
200
|
+
# type: (HTTPRoute, HTTPScope, Receive, Send) -> None
|
|
201
|
+
if sentry_sdk.get_client().get_integration(StarliteIntegration) is None:
|
|
202
|
+
return await old_handle(self, scope, receive, send)
|
|
203
|
+
|
|
204
|
+
sentry_scope = sentry_sdk.get_isolation_scope()
|
|
205
|
+
request = scope["app"].request_class(scope=scope, receive=receive, send=send) # type: Request[Any, Any]
|
|
206
|
+
extracted_request_data = ConnectionDataExtractor(
|
|
207
|
+
parse_body=True, parse_query=True
|
|
208
|
+
)(request)
|
|
209
|
+
body = extracted_request_data.pop("body")
|
|
210
|
+
|
|
211
|
+
request_data = await body
|
|
212
|
+
|
|
213
|
+
def event_processor(event, _):
|
|
214
|
+
# type: (Event, Hint) -> Event
|
|
215
|
+
route_handler = scope.get("route_handler")
|
|
216
|
+
|
|
217
|
+
request_info = event.get("request", {})
|
|
218
|
+
request_info["content_length"] = len(scope.get("_body", b""))
|
|
219
|
+
if should_send_default_pii():
|
|
220
|
+
request_info["cookies"] = extracted_request_data["cookies"]
|
|
221
|
+
if request_data is not None:
|
|
222
|
+
request_info["data"] = request_data
|
|
223
|
+
|
|
224
|
+
func = None
|
|
225
|
+
if route_handler.name is not None:
|
|
226
|
+
tx_name = route_handler.name
|
|
227
|
+
elif isinstance(route_handler.fn, Ref):
|
|
228
|
+
func = route_handler.fn.value
|
|
229
|
+
else:
|
|
230
|
+
func = route_handler.fn
|
|
231
|
+
if func is not None:
|
|
232
|
+
tx_name = transaction_from_function(func)
|
|
233
|
+
|
|
234
|
+
tx_info = {"source": SOURCE_FOR_STYLE["endpoint"]}
|
|
235
|
+
|
|
236
|
+
if not tx_name:
|
|
237
|
+
tx_name = _DEFAULT_TRANSACTION_NAME
|
|
238
|
+
tx_info = {"source": TransactionSource.ROUTE}
|
|
239
|
+
|
|
240
|
+
event.update(
|
|
241
|
+
{
|
|
242
|
+
"request": deepcopy(request_info),
|
|
243
|
+
"transaction": tx_name,
|
|
244
|
+
"transaction_info": tx_info,
|
|
245
|
+
}
|
|
246
|
+
)
|
|
247
|
+
return event
|
|
248
|
+
|
|
249
|
+
sentry_scope._name = StarliteIntegration.identifier
|
|
250
|
+
sentry_scope.add_event_processor(event_processor)
|
|
251
|
+
|
|
252
|
+
return await old_handle(self, scope, receive, send)
|
|
253
|
+
|
|
254
|
+
HTTPRoute.handle = handle_wrapper
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def retrieve_user_from_scope(scope):
|
|
258
|
+
# type: (StarliteScope) -> Optional[dict[str, Any]]
|
|
259
|
+
scope_user = scope.get("user")
|
|
260
|
+
if not scope_user:
|
|
261
|
+
return None
|
|
262
|
+
if isinstance(scope_user, dict):
|
|
263
|
+
return scope_user
|
|
264
|
+
if isinstance(scope_user, BaseModel):
|
|
265
|
+
return scope_user.dict()
|
|
266
|
+
if hasattr(scope_user, "asdict"): # dataclasses
|
|
267
|
+
return scope_user.asdict()
|
|
268
|
+
|
|
269
|
+
plugin = get_plugin_for_value(scope_user)
|
|
270
|
+
if plugin and not is_async_callable(plugin.to_dict):
|
|
271
|
+
return plugin.to_dict(scope_user)
|
|
272
|
+
|
|
273
|
+
return None
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@ensure_integration_enabled(StarliteIntegration)
|
|
277
|
+
def exception_handler(exc, scope, _):
|
|
278
|
+
# type: (Exception, StarliteScope, State) -> None
|
|
279
|
+
user_info = None # type: Optional[dict[str, Any]]
|
|
280
|
+
if should_send_default_pii():
|
|
281
|
+
user_info = retrieve_user_from_scope(scope)
|
|
282
|
+
if user_info and isinstance(user_info, dict):
|
|
283
|
+
sentry_scope = sentry_sdk.get_isolation_scope()
|
|
284
|
+
sentry_scope.set_user(user_info)
|
|
285
|
+
|
|
286
|
+
event, hint = event_from_exception(
|
|
287
|
+
exc,
|
|
288
|
+
client_options=sentry_sdk.get_client().options,
|
|
289
|
+
mechanism={"type": StarliteIntegration.identifier, "handled": False},
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
sentry_sdk.capture_event(event, hint=hint)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
from typing import Any, TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
from sentry_sdk.feature_flags import add_feature_flag
|
|
5
|
+
from sentry_sdk.integrations import Integration, DidNotEnable, _check_minimum_version
|
|
6
|
+
from sentry_sdk.utils import parse_version
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from statsig import statsig as statsig_module
|
|
10
|
+
from statsig.version import __version__ as STATSIG_VERSION
|
|
11
|
+
except ImportError:
|
|
12
|
+
raise DidNotEnable("statsig is not installed")
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from statsig.statsig_user import StatsigUser
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class StatsigIntegration(Integration):
|
|
19
|
+
identifier = "statsig"
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def setup_once():
|
|
23
|
+
# type: () -> None
|
|
24
|
+
version = parse_version(STATSIG_VERSION)
|
|
25
|
+
_check_minimum_version(StatsigIntegration, version, "statsig")
|
|
26
|
+
|
|
27
|
+
# Wrap and patch evaluation method(s) in the statsig module
|
|
28
|
+
old_check_gate = statsig_module.check_gate
|
|
29
|
+
|
|
30
|
+
@wraps(old_check_gate)
|
|
31
|
+
def sentry_check_gate(user, gate, *args, **kwargs):
|
|
32
|
+
# type: (StatsigUser, str, *Any, **Any) -> Any
|
|
33
|
+
enabled = old_check_gate(user, gate, *args, **kwargs)
|
|
34
|
+
add_feature_flag(gate, enabled)
|
|
35
|
+
return enabled
|
|
36
|
+
|
|
37
|
+
statsig_module.check_gate = sentry_check_gate
|
|
@@ -2,16 +2,30 @@ import os
|
|
|
2
2
|
import subprocess
|
|
3
3
|
import sys
|
|
4
4
|
import platform
|
|
5
|
+
from http.client import HTTPConnection
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
import sentry_sdk
|
|
8
|
+
from sentry_sdk.consts import OP, SPANDATA
|
|
7
9
|
from sentry_sdk.integrations import Integration
|
|
8
10
|
from sentry_sdk.scope import add_global_event_processor
|
|
9
|
-
from sentry_sdk.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
from sentry_sdk.tracing_utils import (
|
|
12
|
+
EnvironHeaders,
|
|
13
|
+
should_propagate_trace,
|
|
14
|
+
add_http_request_source,
|
|
15
|
+
)
|
|
16
|
+
from sentry_sdk.utils import (
|
|
17
|
+
SENSITIVE_DATA_SUBSTITUTE,
|
|
18
|
+
capture_internal_exceptions,
|
|
19
|
+
ensure_integration_enabled,
|
|
20
|
+
is_sentry_url,
|
|
21
|
+
logger,
|
|
22
|
+
safe_repr,
|
|
23
|
+
parse_url,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
from typing import TYPE_CHECKING
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
15
29
|
from typing import Any
|
|
16
30
|
from typing import Callable
|
|
17
31
|
from typing import Dict
|
|
@@ -21,17 +35,11 @@ if MYPY:
|
|
|
21
35
|
from sentry_sdk._types import Event, Hint
|
|
22
36
|
|
|
23
37
|
|
|
24
|
-
try:
|
|
25
|
-
from httplib import HTTPConnection # type: ignore
|
|
26
|
-
except ImportError:
|
|
27
|
-
from http.client import HTTPConnection
|
|
28
|
-
|
|
29
|
-
|
|
30
38
|
_RUNTIME_CONTEXT = {
|
|
31
39
|
"name": platform.python_implementation(),
|
|
32
40
|
"version": "%s.%s.%s" % (sys.version_info[:3]),
|
|
33
41
|
"build": sys.version,
|
|
34
|
-
}
|
|
42
|
+
} # type: dict[str, object]
|
|
35
43
|
|
|
36
44
|
|
|
37
45
|
class StdlibIntegration(Integration):
|
|
@@ -46,7 +54,7 @@ class StdlibIntegration(Integration):
|
|
|
46
54
|
@add_global_event_processor
|
|
47
55
|
def add_python_runtime_context(event, hint):
|
|
48
56
|
# type: (Event, Hint) -> Optional[Event]
|
|
49
|
-
if
|
|
57
|
+
if sentry_sdk.get_client().get_integration(StdlibIntegration) is not None:
|
|
50
58
|
contexts = event.setdefault("contexts", {})
|
|
51
59
|
if isinstance(contexts, dict) and "runtime" not in contexts:
|
|
52
60
|
contexts["runtime"] = _RUNTIME_CONTEXT
|
|
@@ -61,16 +69,18 @@ def _install_httplib():
|
|
|
61
69
|
|
|
62
70
|
def putrequest(self, method, url, *args, **kwargs):
|
|
63
71
|
# type: (HTTPConnection, str, str, *Any, **Any) -> Any
|
|
64
|
-
hub = Hub.current
|
|
65
|
-
if hub.get_integration(StdlibIntegration) is None:
|
|
66
|
-
return real_putrequest(self, method, url, *args, **kwargs)
|
|
67
|
-
|
|
68
72
|
host = self.host
|
|
69
73
|
port = self.port
|
|
70
74
|
default_port = self.default_port
|
|
71
75
|
|
|
76
|
+
client = sentry_sdk.get_client()
|
|
77
|
+
if client.get_integration(StdlibIntegration) is None or is_sentry_url(
|
|
78
|
+
client, host
|
|
79
|
+
):
|
|
80
|
+
return real_putrequest(self, method, url, *args, **kwargs)
|
|
81
|
+
|
|
72
82
|
real_url = url
|
|
73
|
-
if not real_url.startswith(("http://", "https://")):
|
|
83
|
+
if real_url is None or not real_url.startswith(("http://", "https://")):
|
|
74
84
|
real_url = "%s://%s%s%s" % (
|
|
75
85
|
default_port == 443 and "https" or "http",
|
|
76
86
|
host,
|
|
@@ -78,17 +88,39 @@ def _install_httplib():
|
|
|
78
88
|
url,
|
|
79
89
|
)
|
|
80
90
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
91
|
+
parsed_url = None
|
|
92
|
+
with capture_internal_exceptions():
|
|
93
|
+
parsed_url = parse_url(real_url, sanitize=False)
|
|
94
|
+
|
|
95
|
+
span = sentry_sdk.start_span(
|
|
96
|
+
op=OP.HTTP_CLIENT,
|
|
97
|
+
name="%s %s"
|
|
98
|
+
% (method, parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE),
|
|
99
|
+
origin="auto.http.stdlib.httplib",
|
|
100
|
+
)
|
|
101
|
+
span.set_data(SPANDATA.HTTP_METHOD, method)
|
|
102
|
+
if parsed_url is not None:
|
|
103
|
+
span.set_data("url", parsed_url.url)
|
|
104
|
+
span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
|
|
105
|
+
span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
|
|
85
106
|
|
|
86
107
|
rv = real_putrequest(self, method, url, *args, **kwargs)
|
|
87
108
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
109
|
+
if should_propagate_trace(client, real_url):
|
|
110
|
+
for (
|
|
111
|
+
key,
|
|
112
|
+
value,
|
|
113
|
+
) in sentry_sdk.get_current_scope().iter_trace_propagation_headers(
|
|
114
|
+
span=span
|
|
115
|
+
):
|
|
116
|
+
logger.debug(
|
|
117
|
+
"[Tracing] Adding `{key}` header {value} to outgoing request to {real_url}.".format(
|
|
118
|
+
key=key, value=value, real_url=real_url
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
self.putheader(key, value)
|
|
122
|
+
|
|
123
|
+
self._sentrysdk_span = span # type: ignore[attr-defined]
|
|
92
124
|
|
|
93
125
|
return rv
|
|
94
126
|
|
|
@@ -99,17 +131,21 @@ def _install_httplib():
|
|
|
99
131
|
if span is None:
|
|
100
132
|
return real_getresponse(self, *args, **kwargs)
|
|
101
133
|
|
|
102
|
-
|
|
134
|
+
try:
|
|
135
|
+
rv = real_getresponse(self, *args, **kwargs)
|
|
103
136
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
137
|
+
span.set_http_status(int(rv.status))
|
|
138
|
+
span.set_data("reason", rv.reason)
|
|
139
|
+
finally:
|
|
140
|
+
span.finish()
|
|
141
|
+
|
|
142
|
+
with capture_internal_exceptions():
|
|
143
|
+
add_http_request_source(span)
|
|
108
144
|
|
|
109
145
|
return rv
|
|
110
146
|
|
|
111
|
-
HTTPConnection.putrequest = putrequest
|
|
112
|
-
HTTPConnection.getresponse = getresponse
|
|
147
|
+
HTTPConnection.putrequest = putrequest # type: ignore[method-assign]
|
|
148
|
+
HTTPConnection.getresponse = getresponse # type: ignore[method-assign]
|
|
113
149
|
|
|
114
150
|
|
|
115
151
|
def _init_argument(args, kwargs, name, position, setdefault_callback=None):
|
|
@@ -147,13 +183,9 @@ def _install_subprocess():
|
|
|
147
183
|
# type: () -> None
|
|
148
184
|
old_popen_init = subprocess.Popen.__init__
|
|
149
185
|
|
|
186
|
+
@ensure_integration_enabled(StdlibIntegration, old_popen_init)
|
|
150
187
|
def sentry_patched_popen_init(self, *a, **kw):
|
|
151
188
|
# type: (subprocess.Popen[Any], *Any, **Any) -> None
|
|
152
|
-
|
|
153
|
-
hub = Hub.current
|
|
154
|
-
if hub.get_integration(StdlibIntegration) is None:
|
|
155
|
-
return old_popen_init(self, *a, **kw) # type: ignore
|
|
156
|
-
|
|
157
189
|
# Convert from tuple to list to be able to set values.
|
|
158
190
|
a = list(a)
|
|
159
191
|
|
|
@@ -178,16 +210,28 @@ def _install_subprocess():
|
|
|
178
210
|
|
|
179
211
|
env = None
|
|
180
212
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
213
|
+
with sentry_sdk.start_span(
|
|
214
|
+
op=OP.SUBPROCESS,
|
|
215
|
+
name=description,
|
|
216
|
+
origin="auto.subprocess.stdlib.subprocess",
|
|
217
|
+
) as span:
|
|
218
|
+
for k, v in sentry_sdk.get_current_scope().iter_trace_propagation_headers(
|
|
219
|
+
span=span
|
|
220
|
+
):
|
|
221
|
+
if env is None:
|
|
222
|
+
env = _init_argument(
|
|
223
|
+
a,
|
|
224
|
+
kw,
|
|
225
|
+
"env",
|
|
226
|
+
10,
|
|
227
|
+
lambda x: dict(x if x is not None else os.environ),
|
|
228
|
+
)
|
|
229
|
+
env["SUBPROCESS_" + k.upper().replace("-", "_")] = v
|
|
185
230
|
|
|
186
|
-
with hub.start_span(op="subprocess", description=description) as span:
|
|
187
231
|
if cwd:
|
|
188
232
|
span.set_data("subprocess.cwd", cwd)
|
|
189
233
|
|
|
190
|
-
rv = old_popen_init(self, *a, **kw)
|
|
234
|
+
rv = old_popen_init(self, *a, **kw)
|
|
191
235
|
|
|
192
236
|
span.set_tag("subprocess.pid", self.pid)
|
|
193
237
|
return rv
|
|
@@ -196,14 +240,13 @@ def _install_subprocess():
|
|
|
196
240
|
|
|
197
241
|
old_popen_wait = subprocess.Popen.wait
|
|
198
242
|
|
|
243
|
+
@ensure_integration_enabled(StdlibIntegration, old_popen_wait)
|
|
199
244
|
def sentry_patched_popen_wait(self, *a, **kw):
|
|
200
245
|
# type: (subprocess.Popen[Any], *Any, **Any) -> Any
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
with hub.start_span(op="subprocess.wait") as span:
|
|
246
|
+
with sentry_sdk.start_span(
|
|
247
|
+
op=OP.SUBPROCESS_WAIT,
|
|
248
|
+
origin="auto.subprocess.stdlib.subprocess",
|
|
249
|
+
) as span:
|
|
207
250
|
span.set_tag("subprocess.pid", self.pid)
|
|
208
251
|
return old_popen_wait(self, *a, **kw)
|
|
209
252
|
|
|
@@ -211,14 +254,13 @@ def _install_subprocess():
|
|
|
211
254
|
|
|
212
255
|
old_popen_communicate = subprocess.Popen.communicate
|
|
213
256
|
|
|
257
|
+
@ensure_integration_enabled(StdlibIntegration, old_popen_communicate)
|
|
214
258
|
def sentry_patched_popen_communicate(self, *a, **kw):
|
|
215
259
|
# type: (subprocess.Popen[Any], *Any, **Any) -> Any
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
with hub.start_span(op="subprocess.communicate") as span:
|
|
260
|
+
with sentry_sdk.start_span(
|
|
261
|
+
op=OP.SUBPROCESS_COMMUNICATE,
|
|
262
|
+
origin="auto.subprocess.stdlib.subprocess",
|
|
263
|
+
) as span:
|
|
222
264
|
span.set_tag("subprocess.pid", self.pid)
|
|
223
265
|
return old_popen_communicate(self, *a, **kw)
|
|
224
266
|
|