sentry-sdk 3.0.0a1__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 +11 -18
- sentry_sdk/_werkzeug.py +5 -7
- sentry_sdk/ai/monitoring.py +44 -31
- sentry_sdk/ai/utils.py +3 -4
- sentry_sdk/api.py +75 -87
- sentry_sdk/attachments.py +10 -12
- sentry_sdk/client.py +137 -155
- sentry_sdk/consts.py +430 -174
- 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 +15 -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 +21 -29
- 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 +41 -61
- sentry_sdk/integrations/celery/beat.py +23 -27
- 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 +27 -33
- sentry_sdk/integrations/dedupe.py +5 -8
- sentry_sdk/integrations/django/__init__.py +57 -72
- sentry_sdk/integrations/django/asgi.py +26 -34
- 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 +19 -1
- 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 +18 -18
- 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 +52 -65
- sentry_sdk/integrations/loguru.py +127 -57
- sentry_sdk/integrations/modules.py +3 -4
- sentry_sdk/integrations/openai.py +100 -88
- 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 +25 -12
- sentry_sdk/integrations/redis/_sync_common.py +19 -13
- 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 +21 -13
- sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +3 -2
- sentry_sdk/integrations/redis/utils.py +23 -24
- 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 +41 -10
- sentry_sdk/monitor.py +16 -28
- sentry_sdk/opentelemetry/consts.py +11 -4
- sentry_sdk/opentelemetry/contextvars_context.py +26 -16
- 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 +48 -58
- sentry_sdk/opentelemetry/tracing.py +58 -14
- sentry_sdk/opentelemetry/utils.py +186 -194
- 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 -273
- 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 +121 -187
- sentry_sdk/tracing_utils.py +104 -122
- sentry_sdk/transport.py +131 -157
- sentry_sdk/utils.py +232 -309
- sentry_sdk/worker.py +16 -28
- {sentry_sdk-3.0.0a1.dist-info → sentry_sdk-3.0.0a3.dist-info}/METADATA +3 -3
- sentry_sdk-3.0.0a3.dist-info/RECORD +168 -0
- {sentry_sdk-3.0.0a1.dist-info → sentry_sdk-3.0.0a3.dist-info}/WHEEL +1 -1
- sentry_sdk-3.0.0a1.dist-info/RECORD +0 -154
- {sentry_sdk-3.0.0a1.dist-info → sentry_sdk-3.0.0a3.dist-info}/entry_points.txt +0 -0
- {sentry_sdk-3.0.0a1.dist-info → sentry_sdk-3.0.0a3.dist-info}/licenses/LICENSE +0 -0
- {sentry_sdk-3.0.0a1.dist-info → sentry_sdk-3.0.0a3.dist-info}/top_level.txt +0 -0
sentry_sdk/integrations/flask.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import sentry_sdk
|
|
2
3
|
from sentry_sdk.consts import SOURCE_FOR_STYLE
|
|
3
4
|
from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
|
|
@@ -57,10 +58,9 @@ class FlaskIntegration(Integration):
|
|
|
57
58
|
|
|
58
59
|
def __init__(
|
|
59
60
|
self,
|
|
60
|
-
transaction_style="endpoint",
|
|
61
|
-
http_methods_to_capture
|
|
62
|
-
):
|
|
63
|
-
# type: (...) -> None
|
|
61
|
+
transaction_style: str = "endpoint",
|
|
62
|
+
http_methods_to_capture: tuple[str, ...] = DEFAULT_HTTP_METHODS_TO_CAPTURE,
|
|
63
|
+
) -> None:
|
|
64
64
|
if transaction_style not in TRANSACTION_STYLE_VALUES:
|
|
65
65
|
raise ValueError(
|
|
66
66
|
"Invalid value for transaction_style: %s (must be in %s)"
|
|
@@ -70,8 +70,7 @@ class FlaskIntegration(Integration):
|
|
|
70
70
|
self.http_methods_to_capture = tuple(map(str.upper, http_methods_to_capture))
|
|
71
71
|
|
|
72
72
|
@staticmethod
|
|
73
|
-
def setup_once():
|
|
74
|
-
# type: () -> None
|
|
73
|
+
def setup_once() -> None:
|
|
75
74
|
try:
|
|
76
75
|
from quart import Quart # type: ignore
|
|
77
76
|
|
|
@@ -93,8 +92,9 @@ class FlaskIntegration(Integration):
|
|
|
93
92
|
|
|
94
93
|
old_app = Flask.__call__
|
|
95
94
|
|
|
96
|
-
def sentry_patched_wsgi_app(
|
|
97
|
-
|
|
95
|
+
def sentry_patched_wsgi_app(
|
|
96
|
+
self: Any, environ: Dict[str, str], start_response: Callable[..., Any]
|
|
97
|
+
) -> _ScopedResponse:
|
|
98
98
|
if sentry_sdk.get_client().get_integration(FlaskIntegration) is None:
|
|
99
99
|
return old_app(self, environ, start_response)
|
|
100
100
|
|
|
@@ -114,8 +114,9 @@ class FlaskIntegration(Integration):
|
|
|
114
114
|
Flask.__call__ = sentry_patched_wsgi_app
|
|
115
115
|
|
|
116
116
|
|
|
117
|
-
def _add_sentry_trace(
|
|
118
|
-
|
|
117
|
+
def _add_sentry_trace(
|
|
118
|
+
sender: Flask, template: Any, context: Dict[str, Any], **extra: Any
|
|
119
|
+
) -> None:
|
|
119
120
|
if "sentry_trace" in context:
|
|
120
121
|
return
|
|
121
122
|
|
|
@@ -125,8 +126,9 @@ def _add_sentry_trace(sender, template, context, **extra):
|
|
|
125
126
|
context["sentry_trace_meta"] = trace_meta
|
|
126
127
|
|
|
127
128
|
|
|
128
|
-
def _set_transaction_name_and_source(
|
|
129
|
-
|
|
129
|
+
def _set_transaction_name_and_source(
|
|
130
|
+
scope: sentry_sdk.Scope, transaction_style: str, request: Request
|
|
131
|
+
) -> None:
|
|
130
132
|
try:
|
|
131
133
|
name_for_style = {
|
|
132
134
|
"url": request.url_rule.rule,
|
|
@@ -140,8 +142,7 @@ def _set_transaction_name_and_source(scope, transaction_style, request):
|
|
|
140
142
|
pass
|
|
141
143
|
|
|
142
144
|
|
|
143
|
-
def _request_started(app, **kwargs):
|
|
144
|
-
# type: (Flask, **Any) -> None
|
|
145
|
+
def _request_started(app: Flask, **kwargs: Any) -> None:
|
|
145
146
|
integration = sentry_sdk.get_client().get_integration(FlaskIntegration)
|
|
146
147
|
if integration is None:
|
|
147
148
|
return
|
|
@@ -160,47 +161,39 @@ def _request_started(app, **kwargs):
|
|
|
160
161
|
|
|
161
162
|
|
|
162
163
|
class FlaskRequestExtractor(RequestExtractor):
|
|
163
|
-
def env(self):
|
|
164
|
-
# type: () -> Dict[str, str]
|
|
164
|
+
def env(self) -> Dict[str, str]:
|
|
165
165
|
return self.request.environ
|
|
166
166
|
|
|
167
|
-
def cookies(self):
|
|
168
|
-
# type: () -> Dict[Any, Any]
|
|
167
|
+
def cookies(self) -> Dict[Any, Any]:
|
|
169
168
|
return {
|
|
170
169
|
k: v[0] if isinstance(v, list) and len(v) == 1 else v
|
|
171
170
|
for k, v in self.request.cookies.items()
|
|
172
171
|
}
|
|
173
172
|
|
|
174
|
-
def raw_data(self):
|
|
175
|
-
# type: () -> bytes
|
|
173
|
+
def raw_data(self) -> bytes:
|
|
176
174
|
return self.request.get_data()
|
|
177
175
|
|
|
178
|
-
def form(self):
|
|
179
|
-
# type: () -> ImmutableMultiDict[str, Any]
|
|
176
|
+
def form(self) -> ImmutableMultiDict[str, Any]:
|
|
180
177
|
return self.request.form
|
|
181
178
|
|
|
182
|
-
def files(self):
|
|
183
|
-
# type: () -> ImmutableMultiDict[str, Any]
|
|
179
|
+
def files(self) -> ImmutableMultiDict[str, Any]:
|
|
184
180
|
return self.request.files
|
|
185
181
|
|
|
186
|
-
def is_json(self):
|
|
187
|
-
# type: () -> bool
|
|
182
|
+
def is_json(self) -> bool:
|
|
188
183
|
return self.request.is_json
|
|
189
184
|
|
|
190
|
-
def json(self):
|
|
191
|
-
# type: () -> Any
|
|
185
|
+
def json(self) -> Any:
|
|
192
186
|
return self.request.get_json(silent=True)
|
|
193
187
|
|
|
194
|
-
def size_of_file(self, file):
|
|
195
|
-
# type: (FileStorage) -> int
|
|
188
|
+
def size_of_file(self, file: FileStorage) -> int:
|
|
196
189
|
return file.content_length
|
|
197
190
|
|
|
198
191
|
|
|
199
|
-
def _make_request_event_processor(
|
|
200
|
-
|
|
192
|
+
def _make_request_event_processor(
|
|
193
|
+
app: Flask, request: Callable[[], Request], integration: FlaskIntegration
|
|
194
|
+
) -> EventProcessor:
|
|
201
195
|
|
|
202
|
-
def inner(event, hint):
|
|
203
|
-
# type: (Event, dict[str, Any]) -> Event
|
|
196
|
+
def inner(event: Event, hint: dict[str, Any]) -> Event:
|
|
204
197
|
|
|
205
198
|
# if the request is gone we are fine not logging the data from
|
|
206
199
|
# it. This might happen if the processor is pushed away to
|
|
@@ -221,8 +214,9 @@ def _make_request_event_processor(app, request, integration):
|
|
|
221
214
|
|
|
222
215
|
|
|
223
216
|
@ensure_integration_enabled(FlaskIntegration)
|
|
224
|
-
def _capture_exception(
|
|
225
|
-
|
|
217
|
+
def _capture_exception(
|
|
218
|
+
sender: Flask, exception: Union[ValueError, BaseException], **kwargs: Any
|
|
219
|
+
) -> None:
|
|
226
220
|
event, hint = event_from_exception(
|
|
227
221
|
exception,
|
|
228
222
|
client_options=sentry_sdk.get_client().options,
|
|
@@ -232,8 +226,7 @@ def _capture_exception(sender, exception, **kwargs):
|
|
|
232
226
|
sentry_sdk.capture_event(event, hint=hint)
|
|
233
227
|
|
|
234
228
|
|
|
235
|
-
def _add_user_to_event(event):
|
|
236
|
-
# type: (Event) -> None
|
|
229
|
+
def _add_user_to_event(event: Event) -> None:
|
|
237
230
|
if flask_login is None:
|
|
238
231
|
return
|
|
239
232
|
|
sentry_sdk/integrations/gcp.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import functools
|
|
2
3
|
import sys
|
|
3
4
|
from copy import deepcopy
|
|
@@ -39,11 +40,11 @@ if TYPE_CHECKING:
|
|
|
39
40
|
F = TypeVar("F", bound=Callable[..., Any])
|
|
40
41
|
|
|
41
42
|
|
|
42
|
-
def _wrap_func(func):
|
|
43
|
-
# type: (F) -> F
|
|
43
|
+
def _wrap_func(func: F) -> F:
|
|
44
44
|
@functools.wraps(func)
|
|
45
|
-
def sentry_func(
|
|
46
|
-
|
|
45
|
+
def sentry_func(
|
|
46
|
+
functionhandler: Any, gcp_event: Any, *args: Any, **kwargs: Any
|
|
47
|
+
) -> Any:
|
|
47
48
|
client = sentry_sdk.get_client()
|
|
48
49
|
|
|
49
50
|
integration = client.get_integration(GcpIntegration)
|
|
@@ -118,13 +119,11 @@ class GcpIntegration(Integration):
|
|
|
118
119
|
identifier = "gcp"
|
|
119
120
|
origin = f"auto.function.{identifier}"
|
|
120
121
|
|
|
121
|
-
def __init__(self, timeout_warning=False):
|
|
122
|
-
# type: (bool) -> None
|
|
122
|
+
def __init__(self, timeout_warning: bool = False) -> None:
|
|
123
123
|
self.timeout_warning = timeout_warning
|
|
124
124
|
|
|
125
125
|
@staticmethod
|
|
126
|
-
def setup_once():
|
|
127
|
-
# type: () -> None
|
|
126
|
+
def setup_once() -> None:
|
|
128
127
|
import __main__ as gcp_functions
|
|
129
128
|
|
|
130
129
|
if not hasattr(gcp_functions, "worker_v1"):
|
|
@@ -140,11 +139,11 @@ class GcpIntegration(Integration):
|
|
|
140
139
|
)
|
|
141
140
|
|
|
142
141
|
|
|
143
|
-
def _make_request_event_processor(
|
|
144
|
-
|
|
142
|
+
def _make_request_event_processor(
|
|
143
|
+
gcp_event: Any, configured_timeout: Any, initial_time: Any
|
|
144
|
+
) -> EventProcessor:
|
|
145
145
|
|
|
146
|
-
def event_processor(event, hint):
|
|
147
|
-
# type: (Event, Hint) -> Optional[Event]
|
|
146
|
+
def event_processor(event: Event, hint: Hint) -> Optional[Event]:
|
|
148
147
|
|
|
149
148
|
final_time = datetime.now(timezone.utc)
|
|
150
149
|
time_diff = final_time - initial_time
|
|
@@ -195,8 +194,7 @@ def _make_request_event_processor(gcp_event, configured_timeout, initial_time):
|
|
|
195
194
|
return event_processor
|
|
196
195
|
|
|
197
196
|
|
|
198
|
-
def _get_google_cloud_logs_url(final_time):
|
|
199
|
-
# type: (datetime) -> str
|
|
197
|
+
def _get_google_cloud_logs_url(final_time: datetime) -> str:
|
|
200
198
|
"""
|
|
201
199
|
Generates a Google Cloud Logs console URL based on the environment variables
|
|
202
200
|
Arguments:
|
|
@@ -238,8 +236,7 @@ EVENT_TO_ATTRIBUTE = {
|
|
|
238
236
|
}
|
|
239
237
|
|
|
240
238
|
|
|
241
|
-
def _prepopulate_attributes(gcp_event):
|
|
242
|
-
# type: (Any) -> dict[str, Any]
|
|
239
|
+
def _prepopulate_attributes(gcp_event: Any) -> dict[str, Any]:
|
|
243
240
|
attributes = {
|
|
244
241
|
"cloud.provider": "gcp",
|
|
245
242
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import re
|
|
2
3
|
|
|
3
4
|
import sentry_sdk
|
|
@@ -38,17 +39,14 @@ class GnuBacktraceIntegration(Integration):
|
|
|
38
39
|
identifier = "gnu_backtrace"
|
|
39
40
|
|
|
40
41
|
@staticmethod
|
|
41
|
-
def setup_once():
|
|
42
|
-
# type: () -> None
|
|
42
|
+
def setup_once() -> None:
|
|
43
43
|
@add_global_event_processor
|
|
44
|
-
def process_gnu_backtrace(event, hint):
|
|
45
|
-
# type: (Event, dict[str, Any]) -> Event
|
|
44
|
+
def process_gnu_backtrace(event: Event, hint: dict[str, Any]) -> Event:
|
|
46
45
|
with capture_internal_exceptions():
|
|
47
46
|
return _process_gnu_backtrace(event, hint)
|
|
48
47
|
|
|
49
48
|
|
|
50
|
-
def _process_gnu_backtrace(event, hint):
|
|
51
|
-
# type: (Event, dict[str, Any]) -> Event
|
|
49
|
+
def _process_gnu_backtrace(event: Event, hint: dict[str, Any]) -> Event:
|
|
52
50
|
if sentry_sdk.get_client().get_integration(GnuBacktraceIntegration) is None:
|
|
53
51
|
return event
|
|
54
52
|
|
sentry_sdk/integrations/gql.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import sentry_sdk
|
|
2
3
|
from sentry_sdk.utils import (
|
|
3
4
|
event_from_exception,
|
|
@@ -34,19 +35,17 @@ class GQLIntegration(Integration):
|
|
|
34
35
|
identifier = "gql"
|
|
35
36
|
|
|
36
37
|
@staticmethod
|
|
37
|
-
def setup_once():
|
|
38
|
-
# type: () -> None
|
|
38
|
+
def setup_once() -> None:
|
|
39
39
|
gql_version = parse_version(gql.__version__)
|
|
40
40
|
_check_minimum_version(GQLIntegration, gql_version)
|
|
41
41
|
|
|
42
42
|
_patch_execute()
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
def _data_from_document(document):
|
|
46
|
-
# type: (DocumentNode) -> EventDataType
|
|
45
|
+
def _data_from_document(document: DocumentNode) -> EventDataType:
|
|
47
46
|
try:
|
|
48
47
|
operation_ast = get_operation_ast(document)
|
|
49
|
-
data = {"query": print_ast(document)}
|
|
48
|
+
data: EventDataType = {"query": print_ast(document)}
|
|
50
49
|
|
|
51
50
|
if operation_ast is not None:
|
|
52
51
|
data["variables"] = operation_ast.variable_definitions
|
|
@@ -58,8 +57,7 @@ def _data_from_document(document):
|
|
|
58
57
|
return dict()
|
|
59
58
|
|
|
60
59
|
|
|
61
|
-
def _transport_method(transport):
|
|
62
|
-
# type: (Union[Transport, AsyncTransport]) -> str
|
|
60
|
+
def _transport_method(transport: Union[Transport, AsyncTransport]) -> str:
|
|
63
61
|
"""
|
|
64
62
|
The RequestsHTTPTransport allows defining the HTTP method; all
|
|
65
63
|
other transports use POST.
|
|
@@ -70,8 +68,9 @@ def _transport_method(transport):
|
|
|
70
68
|
return "POST"
|
|
71
69
|
|
|
72
70
|
|
|
73
|
-
def _request_info_from_transport(
|
|
74
|
-
|
|
71
|
+
def _request_info_from_transport(
|
|
72
|
+
transport: Union[Transport, AsyncTransport, None],
|
|
73
|
+
) -> Dict[str, str]:
|
|
75
74
|
if transport is None:
|
|
76
75
|
return {}
|
|
77
76
|
|
|
@@ -87,13 +86,13 @@ def _request_info_from_transport(transport):
|
|
|
87
86
|
return request_info
|
|
88
87
|
|
|
89
88
|
|
|
90
|
-
def _patch_execute():
|
|
91
|
-
# type: () -> None
|
|
89
|
+
def _patch_execute() -> None:
|
|
92
90
|
real_execute = gql.Client.execute
|
|
93
91
|
|
|
94
92
|
@ensure_integration_enabled(GQLIntegration, real_execute)
|
|
95
|
-
def sentry_patched_execute(
|
|
96
|
-
|
|
93
|
+
def sentry_patched_execute(
|
|
94
|
+
self: gql.Client, document: DocumentNode, *args: Any, **kwargs: Any
|
|
95
|
+
) -> Any:
|
|
97
96
|
scope = sentry_sdk.get_isolation_scope()
|
|
98
97
|
scope.add_event_processor(_make_gql_event_processor(self, document))
|
|
99
98
|
|
|
@@ -112,10 +111,10 @@ def _patch_execute():
|
|
|
112
111
|
gql.Client.execute = sentry_patched_execute
|
|
113
112
|
|
|
114
113
|
|
|
115
|
-
def _make_gql_event_processor(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
114
|
+
def _make_gql_event_processor(
|
|
115
|
+
client: gql.Client, document: DocumentNode
|
|
116
|
+
) -> EventProcessor:
|
|
117
|
+
def processor(event: Event, hint: dict[str, Any]) -> Event:
|
|
119
118
|
try:
|
|
120
119
|
errors = hint["exc_info"][1].errors
|
|
121
120
|
except (AttributeError, KeyError):
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
from contextlib import contextmanager
|
|
2
3
|
|
|
3
4
|
import sentry_sdk
|
|
@@ -31,22 +32,21 @@ class GrapheneIntegration(Integration):
|
|
|
31
32
|
identifier = "graphene"
|
|
32
33
|
|
|
33
34
|
@staticmethod
|
|
34
|
-
def setup_once():
|
|
35
|
-
# type: () -> None
|
|
35
|
+
def setup_once() -> None:
|
|
36
36
|
version = package_version("graphene")
|
|
37
37
|
_check_minimum_version(GrapheneIntegration, version)
|
|
38
38
|
|
|
39
39
|
_patch_graphql()
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
def _patch_graphql():
|
|
43
|
-
# type: () -> None
|
|
42
|
+
def _patch_graphql() -> None:
|
|
44
43
|
old_graphql_sync = graphene_schema.graphql_sync
|
|
45
44
|
old_graphql_async = graphene_schema.graphql
|
|
46
45
|
|
|
47
46
|
@ensure_integration_enabled(GrapheneIntegration, old_graphql_sync)
|
|
48
|
-
def _sentry_patched_graphql_sync(
|
|
49
|
-
|
|
47
|
+
def _sentry_patched_graphql_sync(
|
|
48
|
+
schema: GraphQLSchema, source: Union[str, Source], *args: Any, **kwargs: Any
|
|
49
|
+
) -> ExecutionResult:
|
|
50
50
|
scope = sentry_sdk.get_isolation_scope()
|
|
51
51
|
scope.add_event_processor(_event_processor)
|
|
52
52
|
|
|
@@ -68,8 +68,9 @@ def _patch_graphql():
|
|
|
68
68
|
|
|
69
69
|
return result
|
|
70
70
|
|
|
71
|
-
async def _sentry_patched_graphql_async(
|
|
72
|
-
|
|
71
|
+
async def _sentry_patched_graphql_async(
|
|
72
|
+
schema: GraphQLSchema, source: Union[str, Source], *args: Any, **kwargs: Any
|
|
73
|
+
) -> ExecutionResult:
|
|
73
74
|
integration = sentry_sdk.get_client().get_integration(GrapheneIntegration)
|
|
74
75
|
if integration is None:
|
|
75
76
|
return await old_graphql_async(schema, source, *args, **kwargs)
|
|
@@ -99,8 +100,7 @@ def _patch_graphql():
|
|
|
99
100
|
graphene_schema.graphql = _sentry_patched_graphql_async
|
|
100
101
|
|
|
101
102
|
|
|
102
|
-
def _event_processor(event, hint):
|
|
103
|
-
# type: (Event, Dict[str, Any]) -> Event
|
|
103
|
+
def _event_processor(event: Event, hint: Dict[str, Any]) -> Event:
|
|
104
104
|
if should_send_default_pii():
|
|
105
105
|
request_info = event.setdefault("request", {})
|
|
106
106
|
request_info["api_target"] = "graphql"
|
|
@@ -112,8 +112,9 @@ def _event_processor(event, hint):
|
|
|
112
112
|
|
|
113
113
|
|
|
114
114
|
@contextmanager
|
|
115
|
-
def graphql_span(
|
|
116
|
-
|
|
115
|
+
def graphql_span(
|
|
116
|
+
schema: GraphQLSchema, source: Union[str, Source], kwargs: Dict[str, Any]
|
|
117
|
+
) -> Generator[None, None, None]:
|
|
117
118
|
operation_name = kwargs.get("operation_name")
|
|
118
119
|
|
|
119
120
|
operation_type = "query"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
from functools import wraps
|
|
2
3
|
|
|
3
4
|
import grpc
|
|
@@ -6,6 +7,7 @@ from grpc.aio import Channel as AsyncChannel
|
|
|
6
7
|
from grpc.aio import Server as AsyncServer
|
|
7
8
|
|
|
8
9
|
from sentry_sdk.integrations import Integration
|
|
10
|
+
from sentry_sdk.utils import parse_version
|
|
9
11
|
|
|
10
12
|
from .client import ClientInterceptor
|
|
11
13
|
from .server import ServerInterceptor
|
|
@@ -41,6 +43,8 @@ else:
|
|
|
41
43
|
|
|
42
44
|
P = ParamSpec("P")
|
|
43
45
|
|
|
46
|
+
GRPC_VERSION = parse_version(grpc.__version__)
|
|
47
|
+
|
|
44
48
|
|
|
45
49
|
def _wrap_channel_sync(func: Callable[P, Channel]) -> Callable[P, Channel]:
|
|
46
50
|
"Wrapper for synchronous secure and insecure channel."
|
|
@@ -127,7 +131,21 @@ def _wrap_async_server(func: Callable[P, AsyncServer]) -> Callable[P, AsyncServe
|
|
|
127
131
|
**kwargs: P.kwargs,
|
|
128
132
|
) -> Server:
|
|
129
133
|
server_interceptor = AsyncServerInterceptor()
|
|
130
|
-
interceptors =
|
|
134
|
+
interceptors: Sequence[grpc.ServerInterceptor] = [
|
|
135
|
+
server_interceptor,
|
|
136
|
+
*(interceptors or []),
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
try:
|
|
140
|
+
# We prefer interceptors as a list because of compatibility with
|
|
141
|
+
# opentelemetry https://github.com/getsentry/sentry-python/issues/4389
|
|
142
|
+
# However, prior to grpc 1.42.0, only tuples were accepted, so we
|
|
143
|
+
# have no choice there.
|
|
144
|
+
if GRPC_VERSION is not None and GRPC_VERSION < (1, 42, 0):
|
|
145
|
+
interceptors = tuple(interceptors)
|
|
146
|
+
except Exception:
|
|
147
|
+
pass
|
|
148
|
+
|
|
131
149
|
return func(*args, interceptors=interceptors, **kwargs) # type: ignore
|
|
132
150
|
|
|
133
151
|
return patched_aio_server # type: ignore
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import sentry_sdk
|
|
2
3
|
from sentry_sdk.consts import OP
|
|
3
4
|
from sentry_sdk.integrations import DidNotEnable
|
|
@@ -21,14 +22,19 @@ except ImportError:
|
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
class ServerInterceptor(grpc.aio.ServerInterceptor): # type: ignore
|
|
24
|
-
def __init__(
|
|
25
|
-
|
|
25
|
+
def __init__(
|
|
26
|
+
self: ServerInterceptor,
|
|
27
|
+
find_name: Callable[[ServicerContext], str] | None = None,
|
|
28
|
+
) -> None:
|
|
26
29
|
self._find_method_name = find_name or self._find_name
|
|
27
30
|
|
|
28
31
|
super().__init__()
|
|
29
32
|
|
|
30
|
-
async def intercept_service(
|
|
31
|
-
|
|
33
|
+
async def intercept_service(
|
|
34
|
+
self: ServerInterceptor,
|
|
35
|
+
continuation: Callable[[HandlerCallDetails], Awaitable[RpcMethodHandler]],
|
|
36
|
+
handler_call_details: HandlerCallDetails,
|
|
37
|
+
) -> Optional[Awaitable[RpcMethodHandler]]:
|
|
32
38
|
self._handler_call_details = handler_call_details
|
|
33
39
|
handler = await continuation(handler_call_details)
|
|
34
40
|
if handler is None:
|
|
@@ -37,8 +43,7 @@ class ServerInterceptor(grpc.aio.ServerInterceptor): # type: ignore
|
|
|
37
43
|
if not handler.request_streaming and not handler.response_streaming:
|
|
38
44
|
handler_factory = grpc.unary_unary_rpc_method_handler
|
|
39
45
|
|
|
40
|
-
async def wrapped(request, context):
|
|
41
|
-
# type: (Any, ServicerContext) -> Any
|
|
46
|
+
async def wrapped(request: Any, context: ServicerContext) -> Any:
|
|
42
47
|
name = self._find_method_name(context)
|
|
43
48
|
if not name:
|
|
44
49
|
return await handler(request, context)
|
|
@@ -66,24 +71,21 @@ class ServerInterceptor(grpc.aio.ServerInterceptor): # type: ignore
|
|
|
66
71
|
elif not handler.request_streaming and handler.response_streaming:
|
|
67
72
|
handler_factory = grpc.unary_stream_rpc_method_handler
|
|
68
73
|
|
|
69
|
-
async def wrapped(request, context): # type: ignore
|
|
70
|
-
# type: (Any, ServicerContext) -> Any
|
|
74
|
+
async def wrapped(request: Any, context: ServicerContext) -> Any: # type: ignore
|
|
71
75
|
async for r in handler.unary_stream(request, context):
|
|
72
76
|
yield r
|
|
73
77
|
|
|
74
78
|
elif handler.request_streaming and not handler.response_streaming:
|
|
75
79
|
handler_factory = grpc.stream_unary_rpc_method_handler
|
|
76
80
|
|
|
77
|
-
async def wrapped(request, context):
|
|
78
|
-
# type: (Any, ServicerContext) -> Any
|
|
81
|
+
async def wrapped(request: Any, context: ServicerContext) -> Any:
|
|
79
82
|
response = handler.stream_unary(request, context)
|
|
80
83
|
return await response
|
|
81
84
|
|
|
82
85
|
elif handler.request_streaming and handler.response_streaming:
|
|
83
86
|
handler_factory = grpc.stream_stream_rpc_method_handler
|
|
84
87
|
|
|
85
|
-
async def wrapped(request, context): # type: ignore
|
|
86
|
-
# type: (Any, ServicerContext) -> Any
|
|
88
|
+
async def wrapped(request: Any, context: ServicerContext) -> Any: # type: ignore
|
|
87
89
|
async for r in handler.stream_stream(request, context):
|
|
88
90
|
yield r
|
|
89
91
|
|
|
@@ -93,6 +95,5 @@ class ServerInterceptor(grpc.aio.ServerInterceptor): # type: ignore
|
|
|
93
95
|
response_serializer=handler.response_serializer,
|
|
94
96
|
)
|
|
95
97
|
|
|
96
|
-
def _find_name(self, context):
|
|
97
|
-
# type: (ServicerContext) -> str
|
|
98
|
+
def _find_name(self, context: ServicerContext) -> str:
|
|
98
99
|
return self._handler_call_details.method
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import sentry_sdk
|
|
2
3
|
from sentry_sdk.consts import OP
|
|
3
4
|
from sentry_sdk.integrations import DidNotEnable
|
|
@@ -23,8 +24,12 @@ class ClientInterceptor(
|
|
|
23
24
|
):
|
|
24
25
|
_is_intercepted = False
|
|
25
26
|
|
|
26
|
-
def intercept_unary_unary(
|
|
27
|
-
|
|
27
|
+
def intercept_unary_unary(
|
|
28
|
+
self: ClientInterceptor,
|
|
29
|
+
continuation: Callable[[ClientCallDetails, Message], _UnaryOutcome],
|
|
30
|
+
client_call_details: ClientCallDetails,
|
|
31
|
+
request: Message,
|
|
32
|
+
) -> _UnaryOutcome:
|
|
28
33
|
method = client_call_details.method
|
|
29
34
|
|
|
30
35
|
with sentry_sdk.start_span(
|
|
@@ -45,8 +50,14 @@ class ClientInterceptor(
|
|
|
45
50
|
|
|
46
51
|
return response
|
|
47
52
|
|
|
48
|
-
def intercept_unary_stream(
|
|
49
|
-
|
|
53
|
+
def intercept_unary_stream(
|
|
54
|
+
self: ClientInterceptor,
|
|
55
|
+
continuation: Callable[
|
|
56
|
+
[ClientCallDetails, Message], Union[Iterable[Any], UnaryStreamCall]
|
|
57
|
+
],
|
|
58
|
+
client_call_details: ClientCallDetails,
|
|
59
|
+
request: Message,
|
|
60
|
+
) -> Union[Iterator[Message], Call]:
|
|
50
61
|
method = client_call_details.method
|
|
51
62
|
|
|
52
63
|
with sentry_sdk.start_span(
|
|
@@ -62,17 +73,16 @@ class ClientInterceptor(
|
|
|
62
73
|
client_call_details
|
|
63
74
|
)
|
|
64
75
|
|
|
65
|
-
response = continuation(
|
|
66
|
-
client_call_details, request
|
|
67
|
-
) # type: UnaryStreamCall
|
|
76
|
+
response: UnaryStreamCall = continuation(client_call_details, request)
|
|
68
77
|
# Setting code on unary-stream leads to execution getting stuck
|
|
69
78
|
# span.set_attribute("code", response.code().name)
|
|
70
79
|
|
|
71
80
|
return response
|
|
72
81
|
|
|
73
82
|
@staticmethod
|
|
74
|
-
def _update_client_call_details_metadata_from_scope(
|
|
75
|
-
|
|
83
|
+
def _update_client_call_details_metadata_from_scope(
|
|
84
|
+
client_call_details: ClientCallDetails,
|
|
85
|
+
) -> ClientCallDetails:
|
|
76
86
|
metadata = (
|
|
77
87
|
list(client_call_details.metadata) if client_call_details.metadata else []
|
|
78
88
|
)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import sentry_sdk
|
|
2
3
|
from sentry_sdk.consts import OP
|
|
3
4
|
from sentry_sdk.integrations import DidNotEnable
|
|
@@ -18,20 +19,24 @@ except ImportError:
|
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
class ServerInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
21
|
-
def __init__(
|
|
22
|
-
|
|
22
|
+
def __init__(
|
|
23
|
+
self: ServerInterceptor,
|
|
24
|
+
find_name: Optional[Callable[[ServicerContext], str]] = None,
|
|
25
|
+
) -> None:
|
|
23
26
|
self._find_method_name = find_name or ServerInterceptor._find_name
|
|
24
27
|
|
|
25
28
|
super().__init__()
|
|
26
29
|
|
|
27
|
-
def intercept_service(
|
|
28
|
-
|
|
30
|
+
def intercept_service(
|
|
31
|
+
self: ServerInterceptor,
|
|
32
|
+
continuation: Callable[[HandlerCallDetails], RpcMethodHandler],
|
|
33
|
+
handler_call_details: HandlerCallDetails,
|
|
34
|
+
) -> RpcMethodHandler:
|
|
29
35
|
handler = continuation(handler_call_details)
|
|
30
36
|
if not handler or not handler.unary_unary:
|
|
31
37
|
return handler
|
|
32
38
|
|
|
33
|
-
def behavior(request, context):
|
|
34
|
-
# type: (Message, ServicerContext) -> Message
|
|
39
|
+
def behavior(request: Message, context: ServicerContext) -> Message:
|
|
35
40
|
with sentry_sdk.isolation_scope():
|
|
36
41
|
name = self._find_method_name(context)
|
|
37
42
|
|
|
@@ -59,6 +64,5 @@ class ServerInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
|
59
64
|
)
|
|
60
65
|
|
|
61
66
|
@staticmethod
|
|
62
|
-
def _find_name(context):
|
|
63
|
-
# type: (ServicerContext) -> str
|
|
67
|
+
def _find_name(context: ServicerContext) -> str:
|
|
64
68
|
return context._rpc_event.call_details.method.decode()
|