sentry-sdk 3.0.0a2__py2.py3-none-any.whl → 3.0.0a3__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.
Potentially problematic release.
This version of sentry-sdk might be problematic. Click here for more details.
- sentry_sdk/__init__.py +2 -0
- sentry_sdk/_compat.py +5 -12
- sentry_sdk/_init_implementation.py +7 -7
- sentry_sdk/_log_batcher.py +17 -29
- sentry_sdk/_lru_cache.py +7 -9
- sentry_sdk/_queue.py +2 -4
- sentry_sdk/_types.py +9 -16
- sentry_sdk/_werkzeug.py +5 -7
- sentry_sdk/ai/monitoring.py +40 -28
- sentry_sdk/ai/utils.py +3 -4
- sentry_sdk/api.py +75 -87
- sentry_sdk/attachments.py +10 -12
- sentry_sdk/client.py +110 -153
- sentry_sdk/consts.py +398 -220
- sentry_sdk/crons/api.py +16 -17
- sentry_sdk/crons/decorator.py +25 -27
- sentry_sdk/debug.py +4 -6
- sentry_sdk/envelope.py +46 -112
- sentry_sdk/feature_flags.py +9 -15
- sentry_sdk/integrations/__init__.py +24 -19
- sentry_sdk/integrations/_asgi_common.py +16 -18
- sentry_sdk/integrations/_wsgi_common.py +22 -33
- sentry_sdk/integrations/aiohttp.py +32 -30
- sentry_sdk/integrations/anthropic.py +42 -37
- sentry_sdk/integrations/argv.py +3 -4
- sentry_sdk/integrations/ariadne.py +16 -18
- sentry_sdk/integrations/arq.py +19 -28
- sentry_sdk/integrations/asgi.py +63 -37
- sentry_sdk/integrations/asyncio.py +14 -16
- sentry_sdk/integrations/atexit.py +6 -10
- sentry_sdk/integrations/aws_lambda.py +26 -36
- sentry_sdk/integrations/beam.py +10 -18
- sentry_sdk/integrations/boto3.py +18 -16
- sentry_sdk/integrations/bottle.py +25 -34
- sentry_sdk/integrations/celery/__init__.py +36 -56
- sentry_sdk/integrations/celery/beat.py +22 -26
- sentry_sdk/integrations/celery/utils.py +15 -17
- sentry_sdk/integrations/chalice.py +8 -10
- sentry_sdk/integrations/clickhouse_driver.py +21 -31
- sentry_sdk/integrations/cloud_resource_context.py +9 -16
- sentry_sdk/integrations/cohere.py +17 -23
- sentry_sdk/integrations/dedupe.py +5 -8
- sentry_sdk/integrations/django/__init__.py +57 -72
- sentry_sdk/integrations/django/asgi.py +24 -32
- sentry_sdk/integrations/django/caching.py +23 -19
- sentry_sdk/integrations/django/middleware.py +17 -20
- sentry_sdk/integrations/django/signals_handlers.py +11 -10
- sentry_sdk/integrations/django/templates.py +19 -16
- sentry_sdk/integrations/django/transactions.py +16 -11
- sentry_sdk/integrations/django/views.py +6 -10
- sentry_sdk/integrations/dramatiq.py +21 -21
- sentry_sdk/integrations/excepthook.py +10 -10
- sentry_sdk/integrations/executing.py +3 -4
- sentry_sdk/integrations/falcon.py +27 -42
- sentry_sdk/integrations/fastapi.py +13 -16
- sentry_sdk/integrations/flask.py +31 -38
- sentry_sdk/integrations/gcp.py +13 -16
- sentry_sdk/integrations/gnu_backtrace.py +4 -6
- sentry_sdk/integrations/gql.py +16 -17
- sentry_sdk/integrations/graphene.py +13 -12
- sentry_sdk/integrations/grpc/__init__.py +3 -2
- sentry_sdk/integrations/grpc/aio/server.py +15 -14
- sentry_sdk/integrations/grpc/client.py +19 -9
- sentry_sdk/integrations/grpc/consts.py +2 -0
- sentry_sdk/integrations/grpc/server.py +12 -8
- sentry_sdk/integrations/httpx.py +9 -12
- sentry_sdk/integrations/huey.py +13 -20
- sentry_sdk/integrations/huggingface_hub.py +16 -16
- sentry_sdk/integrations/langchain.py +203 -113
- sentry_sdk/integrations/launchdarkly.py +13 -10
- sentry_sdk/integrations/litestar.py +37 -35
- sentry_sdk/integrations/logging.py +28 -35
- sentry_sdk/integrations/loguru.py +15 -19
- sentry_sdk/integrations/modules.py +3 -4
- sentry_sdk/integrations/openai.py +96 -84
- sentry_sdk/integrations/openai_agents/__init__.py +49 -0
- sentry_sdk/integrations/openai_agents/consts.py +1 -0
- sentry_sdk/integrations/openai_agents/patches/__init__.py +4 -0
- sentry_sdk/integrations/openai_agents/patches/agent_run.py +152 -0
- sentry_sdk/integrations/openai_agents/patches/models.py +52 -0
- sentry_sdk/integrations/openai_agents/patches/runner.py +42 -0
- sentry_sdk/integrations/openai_agents/patches/tools.py +84 -0
- sentry_sdk/integrations/openai_agents/spans/__init__.py +5 -0
- sentry_sdk/integrations/openai_agents/spans/agent_workflow.py +20 -0
- sentry_sdk/integrations/openai_agents/spans/ai_client.py +46 -0
- sentry_sdk/integrations/openai_agents/spans/execute_tool.py +47 -0
- sentry_sdk/integrations/openai_agents/spans/handoff.py +24 -0
- sentry_sdk/integrations/openai_agents/spans/invoke_agent.py +41 -0
- sentry_sdk/integrations/openai_agents/utils.py +201 -0
- sentry_sdk/integrations/openfeature.py +11 -6
- sentry_sdk/integrations/pure_eval.py +6 -10
- sentry_sdk/integrations/pymongo.py +13 -17
- sentry_sdk/integrations/pyramid.py +31 -36
- sentry_sdk/integrations/quart.py +23 -28
- sentry_sdk/integrations/ray.py +73 -64
- sentry_sdk/integrations/redis/__init__.py +7 -4
- sentry_sdk/integrations/redis/_async_common.py +15 -9
- sentry_sdk/integrations/redis/_sync_common.py +13 -12
- sentry_sdk/integrations/redis/modules/caches.py +17 -8
- sentry_sdk/integrations/redis/modules/queries.py +9 -8
- sentry_sdk/integrations/redis/rb.py +3 -2
- sentry_sdk/integrations/redis/redis.py +4 -4
- sentry_sdk/integrations/redis/redis_cluster.py +10 -8
- sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +3 -2
- sentry_sdk/integrations/redis/utils.py +21 -22
- sentry_sdk/integrations/rq.py +13 -16
- sentry_sdk/integrations/rust_tracing.py +9 -6
- sentry_sdk/integrations/sanic.py +34 -46
- sentry_sdk/integrations/serverless.py +22 -27
- sentry_sdk/integrations/socket.py +27 -15
- sentry_sdk/integrations/spark/__init__.py +1 -0
- sentry_sdk/integrations/spark/spark_driver.py +45 -83
- sentry_sdk/integrations/spark/spark_worker.py +7 -11
- sentry_sdk/integrations/sqlalchemy.py +22 -19
- sentry_sdk/integrations/starlette.py +86 -90
- sentry_sdk/integrations/starlite.py +28 -34
- sentry_sdk/integrations/statsig.py +5 -4
- sentry_sdk/integrations/stdlib.py +28 -24
- sentry_sdk/integrations/strawberry.py +62 -49
- sentry_sdk/integrations/sys_exit.py +7 -11
- sentry_sdk/integrations/threading.py +12 -14
- sentry_sdk/integrations/tornado.py +28 -32
- sentry_sdk/integrations/trytond.py +4 -3
- sentry_sdk/integrations/typer.py +8 -6
- sentry_sdk/integrations/unleash.py +5 -4
- sentry_sdk/integrations/wsgi.py +47 -46
- sentry_sdk/logger.py +13 -9
- sentry_sdk/monitor.py +16 -28
- sentry_sdk/opentelemetry/consts.py +11 -4
- sentry_sdk/opentelemetry/contextvars_context.py +17 -15
- sentry_sdk/opentelemetry/propagator.py +38 -21
- sentry_sdk/opentelemetry/sampler.py +51 -34
- sentry_sdk/opentelemetry/scope.py +36 -37
- sentry_sdk/opentelemetry/span_processor.py +43 -59
- sentry_sdk/opentelemetry/tracing.py +32 -12
- sentry_sdk/opentelemetry/utils.py +180 -196
- sentry_sdk/profiler/continuous_profiler.py +108 -97
- sentry_sdk/profiler/transaction_profiler.py +70 -97
- sentry_sdk/profiler/utils.py +11 -15
- sentry_sdk/scope.py +251 -264
- sentry_sdk/scrubber.py +22 -26
- sentry_sdk/serializer.py +40 -54
- sentry_sdk/session.py +44 -61
- sentry_sdk/sessions.py +35 -49
- sentry_sdk/spotlight.py +15 -21
- sentry_sdk/tracing.py +116 -182
- sentry_sdk/tracing_utils.py +100 -120
- sentry_sdk/transport.py +131 -157
- sentry_sdk/utils.py +232 -309
- sentry_sdk/worker.py +16 -28
- {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a3.dist-info}/METADATA +1 -1
- sentry_sdk-3.0.0a3.dist-info/RECORD +168 -0
- sentry_sdk-3.0.0a2.dist-info/RECORD +0 -154
- {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a3.dist-info}/WHEEL +0 -0
- {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a3.dist-info}/entry_points.txt +0 -0
- {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a3.dist-info}/licenses/LICENSE +0 -0
- {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a3.dist-info}/top_level.txt +0 -0
sentry_sdk/integrations/arq.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import sys
|
|
2
3
|
|
|
3
4
|
import sentry_sdk
|
|
@@ -45,8 +46,7 @@ class ArqIntegration(Integration):
|
|
|
45
46
|
origin = f"auto.queue.{identifier}"
|
|
46
47
|
|
|
47
48
|
@staticmethod
|
|
48
|
-
def setup_once():
|
|
49
|
-
# type: () -> None
|
|
49
|
+
def setup_once() -> None:
|
|
50
50
|
|
|
51
51
|
try:
|
|
52
52
|
if isinstance(ARQ_VERSION, str):
|
|
@@ -66,13 +66,13 @@ class ArqIntegration(Integration):
|
|
|
66
66
|
ignore_logger("arq.worker")
|
|
67
67
|
|
|
68
68
|
|
|
69
|
-
def patch_enqueue_job():
|
|
70
|
-
# type: () -> None
|
|
69
|
+
def patch_enqueue_job() -> None:
|
|
71
70
|
old_enqueue_job = ArqRedis.enqueue_job
|
|
72
71
|
original_kwdefaults = old_enqueue_job.__kwdefaults__
|
|
73
72
|
|
|
74
|
-
async def _sentry_enqueue_job(
|
|
75
|
-
|
|
73
|
+
async def _sentry_enqueue_job(
|
|
74
|
+
self: ArqRedis, function: str, *args: Any, **kwargs: Any
|
|
75
|
+
) -> Optional[Job]:
|
|
76
76
|
integration = sentry_sdk.get_client().get_integration(ArqIntegration)
|
|
77
77
|
if integration is None:
|
|
78
78
|
return await old_enqueue_job(self, function, *args, **kwargs)
|
|
@@ -89,12 +89,10 @@ def patch_enqueue_job():
|
|
|
89
89
|
ArqRedis.enqueue_job = _sentry_enqueue_job
|
|
90
90
|
|
|
91
91
|
|
|
92
|
-
def patch_run_job():
|
|
93
|
-
# type: () -> None
|
|
92
|
+
def patch_run_job() -> None:
|
|
94
93
|
old_run_job = Worker.run_job
|
|
95
94
|
|
|
96
|
-
async def _sentry_run_job(self, job_id, score):
|
|
97
|
-
# type: (Worker, str, int) -> None
|
|
95
|
+
async def _sentry_run_job(self: Worker, job_id: str, score: int) -> None:
|
|
98
96
|
integration = sentry_sdk.get_client().get_integration(ArqIntegration)
|
|
99
97
|
if integration is None:
|
|
100
98
|
return await old_run_job(self, job_id, score)
|
|
@@ -123,8 +121,7 @@ def patch_run_job():
|
|
|
123
121
|
Worker.run_job = _sentry_run_job
|
|
124
122
|
|
|
125
123
|
|
|
126
|
-
def _capture_exception(exc_info):
|
|
127
|
-
# type: (ExcInfo) -> None
|
|
124
|
+
def _capture_exception(exc_info: ExcInfo) -> None:
|
|
128
125
|
scope = sentry_sdk.get_current_scope()
|
|
129
126
|
|
|
130
127
|
if scope.root_span is not None:
|
|
@@ -142,10 +139,10 @@ def _capture_exception(exc_info):
|
|
|
142
139
|
sentry_sdk.capture_event(event, hint=hint)
|
|
143
140
|
|
|
144
141
|
|
|
145
|
-
def _make_event_processor(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
142
|
+
def _make_event_processor(
|
|
143
|
+
ctx: Dict[Any, Any], *args: Any, **kwargs: Any
|
|
144
|
+
) -> EventProcessor:
|
|
145
|
+
def event_processor(event: Event, hint: Hint) -> Optional[Event]:
|
|
149
146
|
|
|
150
147
|
with capture_internal_exceptions():
|
|
151
148
|
scope = sentry_sdk.get_current_scope()
|
|
@@ -173,11 +170,9 @@ def _make_event_processor(ctx, *args, **kwargs):
|
|
|
173
170
|
return event_processor
|
|
174
171
|
|
|
175
172
|
|
|
176
|
-
def _wrap_coroutine(name, coroutine):
|
|
177
|
-
# type: (str, WorkerCoroutine) -> WorkerCoroutine
|
|
173
|
+
def _wrap_coroutine(name: str, coroutine: WorkerCoroutine) -> WorkerCoroutine:
|
|
178
174
|
|
|
179
|
-
async def _sentry_coroutine(ctx, *args, **kwargs):
|
|
180
|
-
# type: (Dict[Any, Any], *Any, **Any) -> Any
|
|
175
|
+
async def _sentry_coroutine(ctx: Dict[Any, Any], *args: Any, **kwargs: Any) -> Any:
|
|
181
176
|
integration = sentry_sdk.get_client().get_integration(ArqIntegration)
|
|
182
177
|
if integration is None:
|
|
183
178
|
return await coroutine(ctx, *args, **kwargs)
|
|
@@ -198,13 +193,11 @@ def _wrap_coroutine(name, coroutine):
|
|
|
198
193
|
return _sentry_coroutine
|
|
199
194
|
|
|
200
195
|
|
|
201
|
-
def patch_create_worker():
|
|
202
|
-
# type: () -> None
|
|
196
|
+
def patch_create_worker() -> None:
|
|
203
197
|
old_create_worker = arq.worker.create_worker
|
|
204
198
|
|
|
205
199
|
@ensure_integration_enabled(ArqIntegration, old_create_worker)
|
|
206
|
-
def _sentry_create_worker(*args, **kwargs):
|
|
207
|
-
# type: (*Any, **Any) -> Worker
|
|
200
|
+
def _sentry_create_worker(*args: Any, **kwargs: Any) -> Worker:
|
|
208
201
|
settings_cls = args[0]
|
|
209
202
|
|
|
210
203
|
if isinstance(settings_cls, dict):
|
|
@@ -243,16 +236,14 @@ def patch_create_worker():
|
|
|
243
236
|
arq.worker.create_worker = _sentry_create_worker
|
|
244
237
|
|
|
245
238
|
|
|
246
|
-
def _get_arq_function(func):
|
|
247
|
-
# type: (Union[str, Function, WorkerCoroutine]) -> Function
|
|
239
|
+
def _get_arq_function(func: Union[str, Function, WorkerCoroutine]) -> Function:
|
|
248
240
|
arq_func = arq.worker.func(func)
|
|
249
241
|
arq_func.coroutine = _wrap_coroutine(arq_func.name, arq_func.coroutine)
|
|
250
242
|
|
|
251
243
|
return arq_func
|
|
252
244
|
|
|
253
245
|
|
|
254
|
-
def _get_arq_cron_job(cron_job):
|
|
255
|
-
# type: (CronJob) -> CronJob
|
|
246
|
+
def _get_arq_cron_job(cron_job: CronJob) -> CronJob:
|
|
256
247
|
cron_job.coroutine = _wrap_coroutine(cron_job.name, cron_job.coroutine)
|
|
257
248
|
|
|
258
249
|
return cron_job
|
sentry_sdk/integrations/asgi.py
CHANGED
|
@@ -4,6 +4,7 @@ An ASGI middleware.
|
|
|
4
4
|
Based on Tom Christie's `sentry-asgi <https://github.com/encode/sentry-asgi>`.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from __future__ import annotations
|
|
7
8
|
import asyncio
|
|
8
9
|
import inspect
|
|
9
10
|
from copy import deepcopy
|
|
@@ -61,8 +62,7 @@ ASGI_SCOPE_PROPERTY_TO_ATTRIBUTE = {
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
|
|
64
|
-
def _capture_exception(exc, mechanism_type="asgi"):
|
|
65
|
-
# type: (Any, str) -> None
|
|
65
|
+
def _capture_exception(exc: Any, mechanism_type: str = "asgi") -> None:
|
|
66
66
|
|
|
67
67
|
event, hint = event_from_exception(
|
|
68
68
|
exc,
|
|
@@ -72,8 +72,7 @@ def _capture_exception(exc, mechanism_type="asgi"):
|
|
|
72
72
|
sentry_sdk.capture_event(event, hint=hint)
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
def _looks_like_asgi3(app):
|
|
76
|
-
# type: (Any) -> bool
|
|
75
|
+
def _looks_like_asgi3(app: Any) -> bool:
|
|
77
76
|
"""
|
|
78
77
|
Try to figure out if an application object supports ASGI3.
|
|
79
78
|
|
|
@@ -100,14 +99,13 @@ class SentryAsgiMiddleware:
|
|
|
100
99
|
|
|
101
100
|
def __init__(
|
|
102
101
|
self,
|
|
103
|
-
app
|
|
104
|
-
unsafe_context_data=False,
|
|
105
|
-
transaction_style="endpoint",
|
|
106
|
-
mechanism_type="asgi",
|
|
107
|
-
span_origin
|
|
108
|
-
http_methods_to_capture
|
|
109
|
-
):
|
|
110
|
-
# type: (...) -> None
|
|
102
|
+
app: Any,
|
|
103
|
+
unsafe_context_data: bool = False,
|
|
104
|
+
transaction_style: str = "endpoint",
|
|
105
|
+
mechanism_type: str = "asgi",
|
|
106
|
+
span_origin: Optional[str] = None,
|
|
107
|
+
http_methods_to_capture: Tuple[str, ...] = DEFAULT_HTTP_METHODS_TO_CAPTURE,
|
|
108
|
+
) -> None:
|
|
111
109
|
"""
|
|
112
110
|
Instrument an ASGI application with Sentry. Provides HTTP/websocket
|
|
113
111
|
data to sent events and basic handling for exceptions bubbling up
|
|
@@ -145,24 +143,41 @@ class SentryAsgiMiddleware:
|
|
|
145
143
|
self.http_methods_to_capture = http_methods_to_capture
|
|
146
144
|
|
|
147
145
|
if _looks_like_asgi3(app):
|
|
148
|
-
self.__call__
|
|
146
|
+
self.__call__: Callable[..., Any] = self._run_asgi3
|
|
149
147
|
else:
|
|
150
148
|
self.__call__ = self._run_asgi2
|
|
151
149
|
|
|
152
|
-
def
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
150
|
+
def _capture_lifespan_exception(self, exc: Exception) -> None:
|
|
151
|
+
"""Capture exceptions raise in application lifespan handlers.
|
|
152
|
+
|
|
153
|
+
The separate function is needed to support overriding in derived integrations that use different catching mechanisms.
|
|
154
|
+
"""
|
|
155
|
+
return _capture_exception(exc=exc, mechanism_type=self.mechanism_type)
|
|
156
|
+
|
|
157
|
+
def _capture_request_exception(self, exc: Exception) -> None:
|
|
158
|
+
"""Capture exceptions raised in incoming request handlers.
|
|
159
|
+
|
|
160
|
+
The separate function is needed to support overriding in derived integrations that use different catching mechanisms.
|
|
161
|
+
"""
|
|
162
|
+
return _capture_exception(exc=exc, mechanism_type=self.mechanism_type)
|
|
163
|
+
|
|
164
|
+
def _run_asgi2(self, scope: Any) -> Any:
|
|
165
|
+
async def inner(receive: Any, send: Any) -> Any:
|
|
156
166
|
return await self._run_app(scope, receive, send, asgi_version=2)
|
|
157
167
|
|
|
158
168
|
return inner
|
|
159
169
|
|
|
160
|
-
async def _run_asgi3(self, scope, receive, send):
|
|
161
|
-
# type: (Any, Any, Any) -> Any
|
|
170
|
+
async def _run_asgi3(self, scope: Any, receive: Any, send: Any) -> Any:
|
|
162
171
|
return await self._run_app(scope, receive, send, asgi_version=3)
|
|
163
172
|
|
|
164
|
-
async def _run_original_app(
|
|
165
|
-
|
|
173
|
+
async def _run_original_app(
|
|
174
|
+
self,
|
|
175
|
+
scope: Any,
|
|
176
|
+
receive: Any,
|
|
177
|
+
send: Any,
|
|
178
|
+
asgi_version: Any,
|
|
179
|
+
is_lifespan: int = False,
|
|
180
|
+
) -> Any:
|
|
166
181
|
try:
|
|
167
182
|
if asgi_version == 2:
|
|
168
183
|
return await self.app(scope)(receive, send)
|
|
@@ -170,15 +185,21 @@ class SentryAsgiMiddleware:
|
|
|
170
185
|
return await self.app(scope, receive, send)
|
|
171
186
|
|
|
172
187
|
except Exception as exc:
|
|
173
|
-
|
|
188
|
+
if is_lifespan:
|
|
189
|
+
self._capture_lifespan_exception(exc)
|
|
190
|
+
else:
|
|
191
|
+
self._capture_request_exception(exc)
|
|
174
192
|
raise exc from None
|
|
175
193
|
|
|
176
|
-
async def _run_app(
|
|
177
|
-
|
|
194
|
+
async def _run_app(
|
|
195
|
+
self, scope: Any, receive: Any, send: Any, asgi_version: int
|
|
196
|
+
) -> Any:
|
|
178
197
|
is_recursive_asgi_middleware = _asgi_middleware_applied.get(False)
|
|
179
198
|
is_lifespan = scope["type"] == "lifespan"
|
|
180
199
|
if is_recursive_asgi_middleware or is_lifespan:
|
|
181
|
-
return await self._run_original_app(
|
|
200
|
+
return await self._run_original_app(
|
|
201
|
+
scope, receive, send, asgi_version, is_lifespan
|
|
202
|
+
)
|
|
182
203
|
|
|
183
204
|
_asgi_middleware_applied.set(True)
|
|
184
205
|
try:
|
|
@@ -228,8 +249,9 @@ class SentryAsgiMiddleware:
|
|
|
228
249
|
logger.debug("[ASGI] Started transaction: %s", span)
|
|
229
250
|
span.set_tag("asgi.type", ty)
|
|
230
251
|
|
|
231
|
-
async def _sentry_wrapped_send(
|
|
232
|
-
|
|
252
|
+
async def _sentry_wrapped_send(
|
|
253
|
+
event: Dict[str, Any],
|
|
254
|
+
) -> Any:
|
|
233
255
|
is_http_response = (
|
|
234
256
|
event.get("type") == "http.response.start"
|
|
235
257
|
and span is not None
|
|
@@ -241,13 +263,18 @@ class SentryAsgiMiddleware:
|
|
|
241
263
|
return await send(event)
|
|
242
264
|
|
|
243
265
|
return await self._run_original_app(
|
|
244
|
-
scope,
|
|
266
|
+
scope,
|
|
267
|
+
receive,
|
|
268
|
+
_sentry_wrapped_send,
|
|
269
|
+
asgi_version,
|
|
270
|
+
is_lifespan,
|
|
245
271
|
)
|
|
246
272
|
finally:
|
|
247
273
|
_asgi_middleware_applied.set(False)
|
|
248
274
|
|
|
249
|
-
def event_processor(
|
|
250
|
-
|
|
275
|
+
def event_processor(
|
|
276
|
+
self, event: Event, hint: Hint, asgi_scope: Any
|
|
277
|
+
) -> Optional[Event]:
|
|
251
278
|
request_data = event.get("request", {})
|
|
252
279
|
request_data.update(_get_request_data(asgi_scope))
|
|
253
280
|
event["request"] = deepcopy(request_data)
|
|
@@ -286,11 +313,11 @@ class SentryAsgiMiddleware:
|
|
|
286
313
|
# data to your liking it's recommended to use the `before_send` callback
|
|
287
314
|
# for that.
|
|
288
315
|
|
|
289
|
-
def _get_transaction_name_and_source(
|
|
290
|
-
|
|
316
|
+
def _get_transaction_name_and_source(
|
|
317
|
+
self: SentryAsgiMiddleware, transaction_style: str, asgi_scope: Any
|
|
318
|
+
) -> Tuple[str, str]:
|
|
291
319
|
name = None
|
|
292
320
|
source = SOURCE_FOR_STYLE[transaction_style]
|
|
293
|
-
ty = asgi_scope.get("type")
|
|
294
321
|
|
|
295
322
|
if transaction_style == "endpoint":
|
|
296
323
|
endpoint = asgi_scope.get("endpoint")
|
|
@@ -300,7 +327,7 @@ class SentryAsgiMiddleware:
|
|
|
300
327
|
if endpoint:
|
|
301
328
|
name = transaction_from_function(endpoint) or ""
|
|
302
329
|
else:
|
|
303
|
-
name = _get_url(asgi_scope
|
|
330
|
+
name = _get_url(asgi_scope)
|
|
304
331
|
source = TransactionSource.URL
|
|
305
332
|
|
|
306
333
|
elif transaction_style == "url":
|
|
@@ -312,7 +339,7 @@ class SentryAsgiMiddleware:
|
|
|
312
339
|
if path is not None:
|
|
313
340
|
name = path
|
|
314
341
|
else:
|
|
315
|
-
name = _get_url(asgi_scope
|
|
342
|
+
name = _get_url(asgi_scope)
|
|
316
343
|
source = TransactionSource.URL
|
|
317
344
|
|
|
318
345
|
if name is None:
|
|
@@ -323,8 +350,7 @@ class SentryAsgiMiddleware:
|
|
|
323
350
|
return name, source
|
|
324
351
|
|
|
325
352
|
|
|
326
|
-
def _prepopulate_attributes(scope):
|
|
327
|
-
# type: (Any) -> dict[str, Any]
|
|
353
|
+
def _prepopulate_attributes(scope: Any) -> dict[str, Any]:
|
|
328
354
|
"""Unpack ASGI scope into serializable OTel attributes."""
|
|
329
355
|
scope = scope or {}
|
|
330
356
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import sys
|
|
2
3
|
|
|
3
4
|
import sentry_sdk
|
|
@@ -11,7 +12,7 @@ try:
|
|
|
11
12
|
except ImportError:
|
|
12
13
|
raise DidNotEnable("asyncio not available")
|
|
13
14
|
|
|
14
|
-
from typing import
|
|
15
|
+
from typing import TYPE_CHECKING
|
|
15
16
|
|
|
16
17
|
if TYPE_CHECKING:
|
|
17
18
|
from typing import Any
|
|
@@ -20,8 +21,7 @@ if TYPE_CHECKING:
|
|
|
20
21
|
from sentry_sdk._types import ExcInfo
|
|
21
22
|
|
|
22
23
|
|
|
23
|
-
def get_name(coro):
|
|
24
|
-
# type: (Any) -> str
|
|
24
|
+
def get_name(coro: Any) -> str:
|
|
25
25
|
return (
|
|
26
26
|
getattr(coro, "__qualname__", None)
|
|
27
27
|
or getattr(coro, "__name__", None)
|
|
@@ -29,18 +29,19 @@ def get_name(coro):
|
|
|
29
29
|
)
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
def patch_asyncio():
|
|
33
|
-
# type: () -> None
|
|
32
|
+
def patch_asyncio() -> None:
|
|
34
33
|
orig_task_factory = None
|
|
35
34
|
try:
|
|
36
35
|
loop = asyncio.get_running_loop()
|
|
37
36
|
orig_task_factory = loop.get_task_factory()
|
|
38
37
|
|
|
39
|
-
def _sentry_task_factory(
|
|
40
|
-
|
|
38
|
+
def _sentry_task_factory(
|
|
39
|
+
loop: asyncio.AbstractEventLoop,
|
|
40
|
+
coro: Coroutine[Any, Any, Any],
|
|
41
|
+
**kwargs: Any,
|
|
42
|
+
) -> asyncio.Future[Any]:
|
|
41
43
|
|
|
42
|
-
async def _task_with_sentry_span_creation():
|
|
43
|
-
# type: () -> Any
|
|
44
|
+
async def _task_with_sentry_span_creation() -> Any:
|
|
44
45
|
result = None
|
|
45
46
|
|
|
46
47
|
with sentry_sdk.isolation_scope():
|
|
@@ -79,9 +80,8 @@ def patch_asyncio():
|
|
|
79
80
|
|
|
80
81
|
# Set the task name to include the original coroutine's name
|
|
81
82
|
try:
|
|
82
|
-
|
|
83
|
-
f"{get_name(coro)} (Sentry-wrapped)"
|
|
84
|
-
)
|
|
83
|
+
if isinstance(task, asyncio.Task):
|
|
84
|
+
task.set_name(f"{get_name(coro)} (Sentry-wrapped)")
|
|
85
85
|
except AttributeError:
|
|
86
86
|
# set_name might not be available in all Python versions
|
|
87
87
|
pass
|
|
@@ -100,8 +100,7 @@ def patch_asyncio():
|
|
|
100
100
|
)
|
|
101
101
|
|
|
102
102
|
|
|
103
|
-
def _capture_exception():
|
|
104
|
-
# type: () -> ExcInfo
|
|
103
|
+
def _capture_exception() -> ExcInfo:
|
|
105
104
|
exc_info = sys.exc_info()
|
|
106
105
|
|
|
107
106
|
client = sentry_sdk.get_client()
|
|
@@ -123,6 +122,5 @@ class AsyncioIntegration(Integration):
|
|
|
123
122
|
origin = f"auto.function.{identifier}"
|
|
124
123
|
|
|
125
124
|
@staticmethod
|
|
126
|
-
def setup_once():
|
|
127
|
-
# type: () -> None
|
|
125
|
+
def setup_once() -> None:
|
|
128
126
|
patch_asyncio()
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import os
|
|
2
3
|
import sys
|
|
3
4
|
import atexit
|
|
@@ -12,15 +13,13 @@ if TYPE_CHECKING:
|
|
|
12
13
|
from typing import Optional
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
def default_callback(pending, timeout):
|
|
16
|
-
# type: (int, int) -> None
|
|
16
|
+
def default_callback(pending: int, timeout: int) -> None:
|
|
17
17
|
"""This is the default shutdown callback that is set on the options.
|
|
18
18
|
It prints out a message to stderr that informs the user that some events
|
|
19
19
|
are still pending and the process is waiting for them to flush out.
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
|
-
def echo(msg):
|
|
23
|
-
# type: (str) -> None
|
|
22
|
+
def echo(msg: str) -> None:
|
|
24
23
|
sys.stderr.write(msg + "\n")
|
|
25
24
|
|
|
26
25
|
echo("Sentry is attempting to send %i pending events" % pending)
|
|
@@ -32,18 +31,15 @@ def default_callback(pending, timeout):
|
|
|
32
31
|
class AtexitIntegration(Integration):
|
|
33
32
|
identifier = "atexit"
|
|
34
33
|
|
|
35
|
-
def __init__(self, callback=None):
|
|
36
|
-
# type: (Optional[Any]) -> None
|
|
34
|
+
def __init__(self, callback: Optional[Any] = None) -> None:
|
|
37
35
|
if callback is None:
|
|
38
36
|
callback = default_callback
|
|
39
37
|
self.callback = callback
|
|
40
38
|
|
|
41
39
|
@staticmethod
|
|
42
|
-
def setup_once():
|
|
43
|
-
# type: () -> None
|
|
40
|
+
def setup_once() -> None:
|
|
44
41
|
@atexit.register
|
|
45
|
-
def _shutdown():
|
|
46
|
-
# type: () -> None
|
|
42
|
+
def _shutdown() -> None:
|
|
47
43
|
client = sentry_sdk.get_client()
|
|
48
44
|
integration = client.get_integration(AtexitIntegration)
|
|
49
45
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import functools
|
|
2
3
|
import json
|
|
3
4
|
import re
|
|
@@ -54,11 +55,9 @@ CONTEXT_TO_ATTRIBUTES = {
|
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
|
|
57
|
-
def _wrap_init_error(init_error):
|
|
58
|
-
# type: (F) -> F
|
|
58
|
+
def _wrap_init_error(init_error: F) -> F:
|
|
59
59
|
@ensure_integration_enabled(AwsLambdaIntegration, init_error)
|
|
60
|
-
def sentry_init_error(*args, **kwargs):
|
|
61
|
-
# type: (*Any, **Any) -> Any
|
|
60
|
+
def sentry_init_error(*args: Any, **kwargs: Any) -> Any:
|
|
62
61
|
client = sentry_sdk.get_client()
|
|
63
62
|
|
|
64
63
|
with capture_internal_exceptions():
|
|
@@ -86,11 +85,11 @@ def _wrap_init_error(init_error):
|
|
|
86
85
|
return sentry_init_error # type: ignore
|
|
87
86
|
|
|
88
87
|
|
|
89
|
-
def _wrap_handler(handler):
|
|
90
|
-
# type: (F) -> F
|
|
88
|
+
def _wrap_handler(handler: F) -> F:
|
|
91
89
|
@functools.wraps(handler)
|
|
92
|
-
def sentry_handler(
|
|
93
|
-
|
|
90
|
+
def sentry_handler(
|
|
91
|
+
aws_event: Any, aws_context: Any, *args: Any, **kwargs: Any
|
|
92
|
+
) -> Any:
|
|
94
93
|
|
|
95
94
|
# Per https://docs.aws.amazon.com/lambda/latest/dg/python-handler.html,
|
|
96
95
|
# `event` here is *likely* a dictionary, but also might be a number of
|
|
@@ -192,8 +191,7 @@ def _wrap_handler(handler):
|
|
|
192
191
|
return sentry_handler # type: ignore
|
|
193
192
|
|
|
194
193
|
|
|
195
|
-
def _drain_queue():
|
|
196
|
-
# type: () -> None
|
|
194
|
+
def _drain_queue() -> None:
|
|
197
195
|
with capture_internal_exceptions():
|
|
198
196
|
client = sentry_sdk.get_client()
|
|
199
197
|
integration = client.get_integration(AwsLambdaIntegration)
|
|
@@ -207,13 +205,11 @@ class AwsLambdaIntegration(Integration):
|
|
|
207
205
|
identifier = "aws_lambda"
|
|
208
206
|
origin = f"auto.function.{identifier}"
|
|
209
207
|
|
|
210
|
-
def __init__(self, timeout_warning=False):
|
|
211
|
-
# type: (bool) -> None
|
|
208
|
+
def __init__(self, timeout_warning: bool = False) -> None:
|
|
212
209
|
self.timeout_warning = timeout_warning
|
|
213
210
|
|
|
214
211
|
@staticmethod
|
|
215
|
-
def setup_once():
|
|
216
|
-
# type: () -> None
|
|
212
|
+
def setup_once() -> None:
|
|
217
213
|
|
|
218
214
|
lambda_bootstrap = get_lambda_bootstrap()
|
|
219
215
|
if not lambda_bootstrap:
|
|
@@ -249,10 +245,8 @@ class AwsLambdaIntegration(Integration):
|
|
|
249
245
|
# Patch the runtime client to drain the queue. This should work
|
|
250
246
|
# even when the SDK is initialized inside of the handler
|
|
251
247
|
|
|
252
|
-
def _wrap_post_function(f):
|
|
253
|
-
|
|
254
|
-
def inner(*args, **kwargs):
|
|
255
|
-
# type: (*Any, **Any) -> Any
|
|
248
|
+
def _wrap_post_function(f: F) -> F:
|
|
249
|
+
def inner(*args: Any, **kwargs: Any) -> Any:
|
|
256
250
|
_drain_queue()
|
|
257
251
|
return f(*args, **kwargs)
|
|
258
252
|
|
|
@@ -270,8 +264,7 @@ class AwsLambdaIntegration(Integration):
|
|
|
270
264
|
)
|
|
271
265
|
|
|
272
266
|
|
|
273
|
-
def get_lambda_bootstrap():
|
|
274
|
-
# type: () -> Optional[Any]
|
|
267
|
+
def get_lambda_bootstrap() -> Optional[Any]:
|
|
275
268
|
|
|
276
269
|
# Python 3.7: If the bootstrap module is *already imported*, it is the
|
|
277
270
|
# one we actually want to use (no idea what's in __main__)
|
|
@@ -307,12 +300,14 @@ def get_lambda_bootstrap():
|
|
|
307
300
|
return None
|
|
308
301
|
|
|
309
302
|
|
|
310
|
-
def _make_request_event_processor(
|
|
311
|
-
|
|
303
|
+
def _make_request_event_processor(
|
|
304
|
+
aws_event: Any, aws_context: Any, configured_timeout: Any
|
|
305
|
+
) -> EventProcessor:
|
|
312
306
|
start_time = datetime.now(timezone.utc)
|
|
313
307
|
|
|
314
|
-
def event_processor(
|
|
315
|
-
|
|
308
|
+
def event_processor(
|
|
309
|
+
sentry_event: Event, hint: Hint, start_time: datetime = start_time
|
|
310
|
+
) -> Optional[Event]:
|
|
316
311
|
remaining_time_in_milis = aws_context.get_remaining_time_in_millis()
|
|
317
312
|
exec_duration = configured_timeout - remaining_time_in_milis
|
|
318
313
|
|
|
@@ -375,8 +370,7 @@ def _make_request_event_processor(aws_event, aws_context, configured_timeout):
|
|
|
375
370
|
return event_processor
|
|
376
371
|
|
|
377
372
|
|
|
378
|
-
def _get_url(aws_event, aws_context):
|
|
379
|
-
# type: (Any, Any) -> str
|
|
373
|
+
def _get_url(aws_event: Any, aws_context: Any) -> str:
|
|
380
374
|
path = aws_event.get("path", None)
|
|
381
375
|
|
|
382
376
|
headers = aws_event.get("headers")
|
|
@@ -392,8 +386,7 @@ def _get_url(aws_event, aws_context):
|
|
|
392
386
|
return "awslambda:///{}".format(aws_context.function_name)
|
|
393
387
|
|
|
394
388
|
|
|
395
|
-
def _get_cloudwatch_logs_url(aws_context, start_time):
|
|
396
|
-
# type: (Any, datetime) -> str
|
|
389
|
+
def _get_cloudwatch_logs_url(aws_context: Any, start_time: datetime) -> str:
|
|
397
390
|
"""
|
|
398
391
|
Generates a CloudWatchLogs console URL based on the context object
|
|
399
392
|
|
|
@@ -424,8 +417,7 @@ def _get_cloudwatch_logs_url(aws_context, start_time):
|
|
|
424
417
|
return url
|
|
425
418
|
|
|
426
419
|
|
|
427
|
-
def _parse_formatted_traceback(formatted_tb):
|
|
428
|
-
# type: (list[str]) -> list[dict[str, Any]]
|
|
420
|
+
def _parse_formatted_traceback(formatted_tb: list[str]) -> list[dict[str, Any]]:
|
|
429
421
|
frames = []
|
|
430
422
|
for frame in formatted_tb:
|
|
431
423
|
match = re.match(r'File "(.+)", line (\d+), in (.+)', frame.strip())
|
|
@@ -446,8 +438,7 @@ def _parse_formatted_traceback(formatted_tb):
|
|
|
446
438
|
return frames
|
|
447
439
|
|
|
448
440
|
|
|
449
|
-
def _event_from_error_json(error_json):
|
|
450
|
-
# type: (dict[str, Any]) -> Event
|
|
441
|
+
def _event_from_error_json(error_json: dict[str, Any]) -> Event:
|
|
451
442
|
"""
|
|
452
443
|
Converts the error JSON from AWS Lambda into a Sentry error event.
|
|
453
444
|
This is not a full fletched event, but better than nothing.
|
|
@@ -455,7 +446,7 @@ def _event_from_error_json(error_json):
|
|
|
455
446
|
This is an example of where AWS creates the error JSON:
|
|
456
447
|
https://github.com/aws/aws-lambda-python-runtime-interface-client/blob/2.2.1/awslambdaric/bootstrap.py#L479
|
|
457
448
|
"""
|
|
458
|
-
event = {
|
|
449
|
+
event: Event = {
|
|
459
450
|
"level": "error",
|
|
460
451
|
"exception": {
|
|
461
452
|
"values": [
|
|
@@ -474,13 +465,12 @@ def _event_from_error_json(error_json):
|
|
|
474
465
|
}
|
|
475
466
|
],
|
|
476
467
|
},
|
|
477
|
-
}
|
|
468
|
+
}
|
|
478
469
|
|
|
479
470
|
return event
|
|
480
471
|
|
|
481
472
|
|
|
482
|
-
def _prepopulate_attributes(aws_event, aws_context):
|
|
483
|
-
# type: (Any, Any) -> dict[str, Any]
|
|
473
|
+
def _prepopulate_attributes(aws_event: Any, aws_context: Any) -> dict[str, Any]:
|
|
484
474
|
attributes = {
|
|
485
475
|
"cloud.provider": "aws",
|
|
486
476
|
}
|