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
sentry_sdk/integrations/flask.py
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
from sentry_sdk.integrations import Integration, DidNotEnable
|
|
1
|
+
import sentry_sdk
|
|
2
|
+
from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
|
|
3
|
+
from sentry_sdk.integrations._wsgi_common import (
|
|
4
|
+
DEFAULT_HTTP_METHODS_TO_CAPTURE,
|
|
5
|
+
RequestExtractor,
|
|
6
|
+
)
|
|
8
7
|
from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
|
|
9
|
-
from sentry_sdk.
|
|
8
|
+
from sentry_sdk.scope import should_send_default_pii
|
|
9
|
+
from sentry_sdk.tracing import SOURCE_FOR_STYLE
|
|
10
|
+
from sentry_sdk.utils import (
|
|
11
|
+
capture_internal_exceptions,
|
|
12
|
+
ensure_integration_enabled,
|
|
13
|
+
event_from_exception,
|
|
14
|
+
package_version,
|
|
15
|
+
)
|
|
10
16
|
|
|
11
|
-
from
|
|
17
|
+
from typing import TYPE_CHECKING
|
|
12
18
|
|
|
13
|
-
if
|
|
14
|
-
from
|
|
15
|
-
from typing import Any
|
|
16
|
-
from typing import Dict
|
|
17
|
-
from werkzeug.datastructures import ImmutableTypeConversionDict
|
|
18
|
-
from werkzeug.datastructures import ImmutableMultiDict
|
|
19
|
-
from werkzeug.datastructures import FileStorage
|
|
20
|
-
from typing import Union
|
|
21
|
-
from typing import Callable
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from typing import Any, Callable, Dict, Union
|
|
22
21
|
|
|
23
|
-
from sentry_sdk._types import EventProcessor
|
|
22
|
+
from sentry_sdk._types import Event, EventProcessor
|
|
23
|
+
from sentry_sdk.integrations.wsgi import _ScopedResponse
|
|
24
|
+
from werkzeug.datastructures import FileStorage, ImmutableMultiDict
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
try:
|
|
@@ -29,49 +30,64 @@ except ImportError:
|
|
|
29
30
|
flask_login = None
|
|
30
31
|
|
|
31
32
|
try:
|
|
32
|
-
from flask import
|
|
33
|
-
|
|
34
|
-
Flask,
|
|
35
|
-
_request_ctx_stack,
|
|
36
|
-
_app_ctx_stack,
|
|
37
|
-
__version__ as FLASK_VERSION,
|
|
38
|
-
)
|
|
33
|
+
from flask import Flask, Request # type: ignore
|
|
34
|
+
from flask import request as flask_request
|
|
39
35
|
from flask.signals import (
|
|
36
|
+
before_render_template,
|
|
40
37
|
got_request_exception,
|
|
41
38
|
request_started,
|
|
42
39
|
)
|
|
40
|
+
from markupsafe import Markup
|
|
43
41
|
except ImportError:
|
|
44
42
|
raise DidNotEnable("Flask is not installed")
|
|
45
43
|
|
|
44
|
+
try:
|
|
45
|
+
import blinker # noqa
|
|
46
|
+
except ImportError:
|
|
47
|
+
raise DidNotEnable("blinker is not installed")
|
|
46
48
|
|
|
47
49
|
TRANSACTION_STYLE_VALUES = ("endpoint", "url")
|
|
48
50
|
|
|
49
51
|
|
|
50
52
|
class FlaskIntegration(Integration):
|
|
51
53
|
identifier = "flask"
|
|
54
|
+
origin = f"auto.http.{identifier}"
|
|
52
55
|
|
|
53
|
-
transaction_style =
|
|
56
|
+
transaction_style = ""
|
|
54
57
|
|
|
55
|
-
def __init__(
|
|
56
|
-
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
transaction_style="endpoint", # type: str
|
|
61
|
+
http_methods_to_capture=DEFAULT_HTTP_METHODS_TO_CAPTURE, # type: tuple[str, ...]
|
|
62
|
+
):
|
|
63
|
+
# type: (...) -> None
|
|
57
64
|
if transaction_style not in TRANSACTION_STYLE_VALUES:
|
|
58
65
|
raise ValueError(
|
|
59
66
|
"Invalid value for transaction_style: %s (must be in %s)"
|
|
60
67
|
% (transaction_style, TRANSACTION_STYLE_VALUES)
|
|
61
68
|
)
|
|
62
69
|
self.transaction_style = transaction_style
|
|
70
|
+
self.http_methods_to_capture = tuple(map(str.upper, http_methods_to_capture))
|
|
63
71
|
|
|
64
72
|
@staticmethod
|
|
65
73
|
def setup_once():
|
|
66
74
|
# type: () -> None
|
|
67
75
|
try:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
76
|
+
from quart import Quart # type: ignore
|
|
77
|
+
|
|
78
|
+
if Flask == Quart:
|
|
79
|
+
# This is Quart masquerading as Flask, don't enable the Flask
|
|
80
|
+
# integration. See https://github.com/getsentry/sentry-python/issues/2709
|
|
81
|
+
raise DidNotEnable(
|
|
82
|
+
"This is not a Flask app but rather Quart pretending to be Flask"
|
|
83
|
+
)
|
|
84
|
+
except ImportError:
|
|
85
|
+
pass
|
|
71
86
|
|
|
72
|
-
|
|
73
|
-
|
|
87
|
+
version = package_version("flask")
|
|
88
|
+
_check_minimum_version(FlaskIntegration, version)
|
|
74
89
|
|
|
90
|
+
before_render_template.connect(_add_sentry_trace)
|
|
75
91
|
request_started.connect(_request_started)
|
|
76
92
|
got_request_exception.connect(_capture_exception)
|
|
77
93
|
|
|
@@ -79,41 +95,68 @@ class FlaskIntegration(Integration):
|
|
|
79
95
|
|
|
80
96
|
def sentry_patched_wsgi_app(self, environ, start_response):
|
|
81
97
|
# type: (Any, Dict[str, str], Callable[..., Any]) -> _ScopedResponse
|
|
82
|
-
if
|
|
98
|
+
if sentry_sdk.get_client().get_integration(FlaskIntegration) is None:
|
|
83
99
|
return old_app(self, environ, start_response)
|
|
84
100
|
|
|
85
|
-
|
|
86
|
-
|
|
101
|
+
integration = sentry_sdk.get_client().get_integration(FlaskIntegration)
|
|
102
|
+
|
|
103
|
+
middleware = SentryWsgiMiddleware(
|
|
104
|
+
lambda *a, **kw: old_app(self, *a, **kw),
|
|
105
|
+
span_origin=FlaskIntegration.origin,
|
|
106
|
+
http_methods_to_capture=(
|
|
107
|
+
integration.http_methods_to_capture
|
|
108
|
+
if integration
|
|
109
|
+
else DEFAULT_HTTP_METHODS_TO_CAPTURE
|
|
110
|
+
),
|
|
87
111
|
)
|
|
112
|
+
return middleware(environ, start_response)
|
|
88
113
|
|
|
89
|
-
Flask.__call__ = sentry_patched_wsgi_app
|
|
114
|
+
Flask.__call__ = sentry_patched_wsgi_app
|
|
90
115
|
|
|
91
116
|
|
|
92
|
-
def
|
|
117
|
+
def _add_sentry_trace(sender, template, context, **extra):
|
|
118
|
+
# type: (Flask, Any, Dict[str, Any], **Any) -> None
|
|
119
|
+
if "sentry_trace" in context:
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
scope = sentry_sdk.get_current_scope()
|
|
123
|
+
trace_meta = Markup(scope.trace_propagation_meta())
|
|
124
|
+
context["sentry_trace"] = trace_meta # for backwards compatibility
|
|
125
|
+
context["sentry_trace_meta"] = trace_meta
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _set_transaction_name_and_source(scope, transaction_style, request):
|
|
129
|
+
# type: (sentry_sdk.Scope, str, Request) -> None
|
|
130
|
+
try:
|
|
131
|
+
name_for_style = {
|
|
132
|
+
"url": request.url_rule.rule,
|
|
133
|
+
"endpoint": request.url_rule.endpoint,
|
|
134
|
+
}
|
|
135
|
+
scope.set_transaction_name(
|
|
136
|
+
name_for_style[transaction_style],
|
|
137
|
+
source=SOURCE_FOR_STYLE[transaction_style],
|
|
138
|
+
)
|
|
139
|
+
except Exception:
|
|
140
|
+
pass
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _request_started(app, **kwargs):
|
|
93
144
|
# type: (Flask, **Any) -> None
|
|
94
|
-
|
|
95
|
-
integration = hub.get_integration(FlaskIntegration)
|
|
145
|
+
integration = sentry_sdk.get_client().get_integration(FlaskIntegration)
|
|
96
146
|
if integration is None:
|
|
97
147
|
return
|
|
98
148
|
|
|
99
|
-
|
|
100
|
-
with hub.configure_scope() as scope:
|
|
101
|
-
request = _request_ctx_stack.top.request
|
|
149
|
+
request = flask_request._get_current_object()
|
|
102
150
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
scope.transaction = request.url_rule.rule
|
|
109
|
-
except Exception:
|
|
110
|
-
pass
|
|
151
|
+
# Set the transaction name and source here,
|
|
152
|
+
# but rely on WSGI middleware to actually start the transaction
|
|
153
|
+
_set_transaction_name_and_source(
|
|
154
|
+
sentry_sdk.get_current_scope(), integration.transaction_style, request
|
|
155
|
+
)
|
|
111
156
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
)
|
|
116
|
-
scope.add_event_processor(evt_processor)
|
|
157
|
+
scope = sentry_sdk.get_isolation_scope()
|
|
158
|
+
evt_processor = _make_request_event_processor(app, request, integration)
|
|
159
|
+
scope.add_event_processor(evt_processor)
|
|
117
160
|
|
|
118
161
|
|
|
119
162
|
class FlaskRequestExtractor(RequestExtractor):
|
|
@@ -122,8 +165,11 @@ class FlaskRequestExtractor(RequestExtractor):
|
|
|
122
165
|
return self.request.environ
|
|
123
166
|
|
|
124
167
|
def cookies(self):
|
|
125
|
-
# type: () ->
|
|
126
|
-
return
|
|
168
|
+
# type: () -> Dict[Any, Any]
|
|
169
|
+
return {
|
|
170
|
+
k: v[0] if isinstance(v, list) and len(v) == 1 else v
|
|
171
|
+
for k, v in self.request.cookies.items()
|
|
172
|
+
}
|
|
127
173
|
|
|
128
174
|
def raw_data(self):
|
|
129
175
|
# type: () -> bytes
|
|
@@ -143,18 +189,18 @@ class FlaskRequestExtractor(RequestExtractor):
|
|
|
143
189
|
|
|
144
190
|
def json(self):
|
|
145
191
|
# type: () -> Any
|
|
146
|
-
return self.request.get_json()
|
|
192
|
+
return self.request.get_json(silent=True)
|
|
147
193
|
|
|
148
194
|
def size_of_file(self, file):
|
|
149
195
|
# type: (FileStorage) -> int
|
|
150
196
|
return file.content_length
|
|
151
197
|
|
|
152
198
|
|
|
153
|
-
def _make_request_event_processor(app,
|
|
199
|
+
def _make_request_event_processor(app, request, integration):
|
|
154
200
|
# type: (Flask, Callable[[], Request], FlaskIntegration) -> EventProcessor
|
|
201
|
+
|
|
155
202
|
def inner(event, hint):
|
|
156
|
-
# type: (
|
|
157
|
-
request = weak_request()
|
|
203
|
+
# type: (Event, dict[str, Any]) -> Event
|
|
158
204
|
|
|
159
205
|
# if the request is gone we are fine not logging the data from
|
|
160
206
|
# it. This might happen if the processor is pushed away to
|
|
@@ -165,7 +211,7 @@ def _make_request_event_processor(app, weak_request, integration):
|
|
|
165
211
|
with capture_internal_exceptions():
|
|
166
212
|
FlaskRequestExtractor(request).extract_into_event(event)
|
|
167
213
|
|
|
168
|
-
if
|
|
214
|
+
if should_send_default_pii():
|
|
169
215
|
with capture_internal_exceptions():
|
|
170
216
|
_add_user_to_event(event)
|
|
171
217
|
|
|
@@ -174,26 +220,20 @@ def _make_request_event_processor(app, weak_request, integration):
|
|
|
174
220
|
return inner
|
|
175
221
|
|
|
176
222
|
|
|
223
|
+
@ensure_integration_enabled(FlaskIntegration)
|
|
177
224
|
def _capture_exception(sender, exception, **kwargs):
|
|
178
225
|
# type: (Flask, Union[ValueError, BaseException], **Any) -> None
|
|
179
|
-
hub = Hub.current
|
|
180
|
-
if hub.get_integration(FlaskIntegration) is None:
|
|
181
|
-
return
|
|
182
|
-
|
|
183
|
-
# If an integration is there, a client has to be there.
|
|
184
|
-
client = hub.client # type: Any
|
|
185
|
-
|
|
186
226
|
event, hint = event_from_exception(
|
|
187
227
|
exception,
|
|
188
|
-
client_options=
|
|
228
|
+
client_options=sentry_sdk.get_client().options,
|
|
189
229
|
mechanism={"type": "flask", "handled": False},
|
|
190
230
|
)
|
|
191
231
|
|
|
192
|
-
|
|
232
|
+
sentry_sdk.capture_event(event, hint=hint)
|
|
193
233
|
|
|
194
234
|
|
|
195
235
|
def _add_user_to_event(event):
|
|
196
|
-
# type: (
|
|
236
|
+
# type: (Event) -> None
|
|
197
237
|
if flask_login is None:
|
|
198
238
|
return
|
|
199
239
|
|
|
@@ -231,6 +271,5 @@ def _add_user_to_event(event):
|
|
|
231
271
|
|
|
232
272
|
try:
|
|
233
273
|
user_info.setdefault("username", user.username)
|
|
234
|
-
user_info.setdefault("username", user.email)
|
|
235
274
|
except Exception:
|
|
236
275
|
pass
|
sentry_sdk/integrations/gcp.py
CHANGED
|
@@ -1,27 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
from os import environ
|
|
1
|
+
import functools
|
|
3
2
|
import sys
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
from datetime import datetime, timedelta, timezone
|
|
5
|
+
from os import environ
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
from sentry_sdk.
|
|
7
|
-
from sentry_sdk.
|
|
7
|
+
import sentry_sdk
|
|
8
|
+
from sentry_sdk.api import continue_trace
|
|
9
|
+
from sentry_sdk.consts import OP
|
|
10
|
+
from sentry_sdk.integrations import Integration
|
|
11
|
+
from sentry_sdk.integrations._wsgi_common import _filter_headers
|
|
12
|
+
from sentry_sdk.scope import should_send_default_pii
|
|
13
|
+
from sentry_sdk.tracing import TransactionSource
|
|
8
14
|
from sentry_sdk.utils import (
|
|
9
15
|
AnnotatedValue,
|
|
10
16
|
capture_internal_exceptions,
|
|
11
17
|
event_from_exception,
|
|
12
18
|
logger,
|
|
13
19
|
TimeoutThread,
|
|
20
|
+
reraise,
|
|
14
21
|
)
|
|
15
|
-
from sentry_sdk.integrations import Integration
|
|
16
|
-
from sentry_sdk.integrations._wsgi_common import _filter_headers
|
|
17
22
|
|
|
18
|
-
from
|
|
23
|
+
from typing import TYPE_CHECKING
|
|
19
24
|
|
|
20
25
|
# Constants
|
|
21
26
|
TIMEOUT_WARNING_BUFFER = 1.5 # Buffer time required to send timeout warning to Sentry
|
|
22
27
|
MILLIS_TO_SECONDS = 1000.0
|
|
23
28
|
|
|
24
|
-
if
|
|
29
|
+
if TYPE_CHECKING:
|
|
25
30
|
from typing import Any
|
|
26
31
|
from typing import TypeVar
|
|
27
32
|
from typing import Callable
|
|
@@ -34,73 +39,99 @@ if MYPY:
|
|
|
34
39
|
|
|
35
40
|
def _wrap_func(func):
|
|
36
41
|
# type: (F) -> F
|
|
37
|
-
|
|
42
|
+
@functools.wraps(func)
|
|
43
|
+
def sentry_func(functionhandler, gcp_event, *args, **kwargs):
|
|
38
44
|
# type: (Any, Any, *Any, **Any) -> Any
|
|
45
|
+
client = sentry_sdk.get_client()
|
|
39
46
|
|
|
40
|
-
|
|
41
|
-
integration = hub.get_integration(GcpIntegration)
|
|
47
|
+
integration = client.get_integration(GcpIntegration)
|
|
42
48
|
if integration is None:
|
|
43
|
-
return func(functionhandler,
|
|
44
|
-
|
|
45
|
-
# If an integration is there, a client has to be there.
|
|
46
|
-
client = hub.client # type: Any
|
|
49
|
+
return func(functionhandler, gcp_event, *args, **kwargs)
|
|
47
50
|
|
|
48
51
|
configured_time = environ.get("FUNCTION_TIMEOUT_SEC")
|
|
49
52
|
if not configured_time:
|
|
50
53
|
logger.debug(
|
|
51
54
|
"The configured timeout could not be fetched from Cloud Functions configuration."
|
|
52
55
|
)
|
|
53
|
-
return func(functionhandler,
|
|
56
|
+
return func(functionhandler, gcp_event, *args, **kwargs)
|
|
54
57
|
|
|
55
58
|
configured_time = int(configured_time)
|
|
56
59
|
|
|
57
|
-
initial_time = datetime.
|
|
60
|
+
initial_time = datetime.now(timezone.utc)
|
|
58
61
|
|
|
59
|
-
with
|
|
62
|
+
with sentry_sdk.isolation_scope() as scope:
|
|
60
63
|
with capture_internal_exceptions():
|
|
61
64
|
scope.clear_breadcrumbs()
|
|
62
65
|
scope.add_event_processor(
|
|
63
|
-
_make_request_event_processor(
|
|
66
|
+
_make_request_event_processor(
|
|
67
|
+
gcp_event, configured_time, initial_time
|
|
68
|
+
)
|
|
64
69
|
)
|
|
65
70
|
scope.set_tag("gcp_region", environ.get("FUNCTION_REGION"))
|
|
71
|
+
timeout_thread = None
|
|
66
72
|
if (
|
|
67
73
|
integration.timeout_warning
|
|
68
74
|
and configured_time > TIMEOUT_WARNING_BUFFER
|
|
69
75
|
):
|
|
70
76
|
waiting_time = configured_time - TIMEOUT_WARNING_BUFFER
|
|
71
77
|
|
|
72
|
-
timeout_thread = TimeoutThread(
|
|
78
|
+
timeout_thread = TimeoutThread(
|
|
79
|
+
waiting_time,
|
|
80
|
+
configured_time,
|
|
81
|
+
isolation_scope=scope,
|
|
82
|
+
current_scope=sentry_sdk.get_current_scope(),
|
|
83
|
+
)
|
|
73
84
|
|
|
74
85
|
# Starting the thread to raise timeout warning exception
|
|
75
86
|
timeout_thread.start()
|
|
76
87
|
|
|
77
88
|
headers = {}
|
|
78
|
-
if hasattr(
|
|
79
|
-
headers =
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
if hasattr(gcp_event, "headers"):
|
|
90
|
+
headers = gcp_event.headers
|
|
91
|
+
|
|
92
|
+
transaction = continue_trace(
|
|
93
|
+
headers,
|
|
94
|
+
op=OP.FUNCTION_GCP,
|
|
95
|
+
name=environ.get("FUNCTION_NAME", ""),
|
|
96
|
+
source=TransactionSource.COMPONENT,
|
|
97
|
+
origin=GcpIntegration.origin,
|
|
82
98
|
)
|
|
83
|
-
|
|
99
|
+
sampling_context = {
|
|
100
|
+
"gcp_env": {
|
|
101
|
+
"function_name": environ.get("FUNCTION_NAME"),
|
|
102
|
+
"function_entry_point": environ.get("ENTRY_POINT"),
|
|
103
|
+
"function_identity": environ.get("FUNCTION_IDENTITY"),
|
|
104
|
+
"function_region": environ.get("FUNCTION_REGION"),
|
|
105
|
+
"function_project": environ.get("GCP_PROJECT"),
|
|
106
|
+
},
|
|
107
|
+
"gcp_event": gcp_event,
|
|
108
|
+
}
|
|
109
|
+
with sentry_sdk.start_transaction(
|
|
110
|
+
transaction, custom_sampling_context=sampling_context
|
|
111
|
+
):
|
|
84
112
|
try:
|
|
85
|
-
return func(functionhandler,
|
|
113
|
+
return func(functionhandler, gcp_event, *args, **kwargs)
|
|
86
114
|
except Exception:
|
|
87
115
|
exc_info = sys.exc_info()
|
|
88
|
-
|
|
116
|
+
sentry_event, hint = event_from_exception(
|
|
89
117
|
exc_info,
|
|
90
118
|
client_options=client.options,
|
|
91
119
|
mechanism={"type": "gcp", "handled": False},
|
|
92
120
|
)
|
|
93
|
-
|
|
121
|
+
sentry_sdk.capture_event(sentry_event, hint=hint)
|
|
94
122
|
reraise(*exc_info)
|
|
95
123
|
finally:
|
|
124
|
+
if timeout_thread:
|
|
125
|
+
timeout_thread.stop()
|
|
96
126
|
# Flush out the event queue
|
|
97
|
-
|
|
127
|
+
client.flush()
|
|
98
128
|
|
|
99
129
|
return sentry_func # type: ignore
|
|
100
130
|
|
|
101
131
|
|
|
102
132
|
class GcpIntegration(Integration):
|
|
103
133
|
identifier = "gcp"
|
|
134
|
+
origin = f"auto.function.{identifier}"
|
|
104
135
|
|
|
105
136
|
def __init__(self, timeout_warning=False):
|
|
106
137
|
# type: (bool) -> None
|
|
@@ -109,7 +140,7 @@ class GcpIntegration(Integration):
|
|
|
109
140
|
@staticmethod
|
|
110
141
|
def setup_once():
|
|
111
142
|
# type: () -> None
|
|
112
|
-
import __main__ as gcp_functions
|
|
143
|
+
import __main__ as gcp_functions
|
|
113
144
|
|
|
114
145
|
if not hasattr(gcp_functions, "worker_v1"):
|
|
115
146
|
logger.warning(
|
|
@@ -130,10 +161,10 @@ def _make_request_event_processor(gcp_event, configured_timeout, initial_time):
|
|
|
130
161
|
def event_processor(event, hint):
|
|
131
162
|
# type: (Event, Hint) -> Optional[Event]
|
|
132
163
|
|
|
133
|
-
final_time = datetime.
|
|
164
|
+
final_time = datetime.now(timezone.utc)
|
|
134
165
|
time_diff = final_time - initial_time
|
|
135
166
|
|
|
136
|
-
execution_duration_in_millis = time_diff
|
|
167
|
+
execution_duration_in_millis = time_diff / timedelta(milliseconds=1)
|
|
137
168
|
|
|
138
169
|
extra = event.setdefault("extra", {})
|
|
139
170
|
extra["google cloud functions"] = {
|
|
@@ -163,16 +194,16 @@ def _make_request_event_processor(gcp_event, configured_timeout, initial_time):
|
|
|
163
194
|
if hasattr(gcp_event, "headers"):
|
|
164
195
|
request["headers"] = _filter_headers(gcp_event.headers)
|
|
165
196
|
|
|
166
|
-
if
|
|
197
|
+
if should_send_default_pii():
|
|
167
198
|
if hasattr(gcp_event, "data"):
|
|
168
199
|
request["data"] = gcp_event.data
|
|
169
200
|
else:
|
|
170
201
|
if hasattr(gcp_event, "data"):
|
|
171
202
|
# Unfortunately couldn't find a way to get structured body from GCP
|
|
172
203
|
# event. Meaning every body is unstructured to us.
|
|
173
|
-
request["data"] = AnnotatedValue(
|
|
204
|
+
request["data"] = AnnotatedValue.removed_because_raw_data()
|
|
174
205
|
|
|
175
|
-
event["request"] = request
|
|
206
|
+
event["request"] = deepcopy(request)
|
|
176
207
|
|
|
177
208
|
return event
|
|
178
209
|
|
|
@@ -1,34 +1,26 @@
|
|
|
1
1
|
import re
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import sentry_sdk
|
|
4
4
|
from sentry_sdk.integrations import Integration
|
|
5
5
|
from sentry_sdk.scope import add_global_event_processor
|
|
6
6
|
from sentry_sdk.utils import capture_internal_exceptions
|
|
7
7
|
|
|
8
|
-
from
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
9
|
|
|
10
|
-
if
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
11
|
from typing import Any
|
|
12
|
-
from
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
MODULE_RE = r"[a-zA-Z0-9/._:\\-]+"
|
|
16
|
-
TYPE_RE = r"[a-zA-Z0-9._:<>,-]+"
|
|
17
|
-
HEXVAL_RE = r"[A-Fa-f0-9]+"
|
|
12
|
+
from sentry_sdk._types import Event
|
|
18
13
|
|
|
14
|
+
# function is everything between index at @
|
|
15
|
+
# and then we match on the @ plus the hex val
|
|
16
|
+
FUNCTION_RE = r"[^@]+?"
|
|
17
|
+
HEX_ADDRESS = r"\s+@\s+0x[0-9a-fA-F]+"
|
|
19
18
|
|
|
20
19
|
FRAME_RE = r"""
|
|
21
|
-
^(?P<index>\d+)\.\s
|
|
22
|
-
(?P<package>{MODULE_RE})\(
|
|
23
|
-
(?P<retval>{TYPE_RE}\ )?
|
|
24
|
-
((?P<function>{TYPE_RE})
|
|
25
|
-
(?P<args>\(.*\))?
|
|
26
|
-
)?
|
|
27
|
-
((?P<constoffset>\ const)?\+0x(?P<offset>{HEXVAL_RE}))?
|
|
28
|
-
\)\s
|
|
29
|
-
\[0x(?P<retaddr>{HEXVAL_RE})\]$
|
|
20
|
+
^(?P<index>\d+)\.\s+(?P<function>{FUNCTION_RE}){HEX_ADDRESS}(?:\s+in\s+(?P<package>.+))?$
|
|
30
21
|
""".format(
|
|
31
|
-
|
|
22
|
+
FUNCTION_RE=FUNCTION_RE,
|
|
23
|
+
HEX_ADDRESS=HEX_ADDRESS,
|
|
32
24
|
)
|
|
33
25
|
|
|
34
26
|
FRAME_RE = re.compile(FRAME_RE, re.MULTILINE | re.VERBOSE)
|
|
@@ -42,14 +34,14 @@ class GnuBacktraceIntegration(Integration):
|
|
|
42
34
|
# type: () -> None
|
|
43
35
|
@add_global_event_processor
|
|
44
36
|
def process_gnu_backtrace(event, hint):
|
|
45
|
-
# type: (
|
|
37
|
+
# type: (Event, dict[str, Any]) -> Event
|
|
46
38
|
with capture_internal_exceptions():
|
|
47
39
|
return _process_gnu_backtrace(event, hint)
|
|
48
40
|
|
|
49
41
|
|
|
50
42
|
def _process_gnu_backtrace(event, hint):
|
|
51
|
-
# type: (
|
|
52
|
-
if
|
|
43
|
+
# type: (Event, dict[str, Any]) -> Event
|
|
44
|
+
if sentry_sdk.get_client().get_integration(GnuBacktraceIntegration) is None:
|
|
53
45
|
return event
|
|
54
46
|
|
|
55
47
|
exc_info = hint.get("exc_info", None)
|