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.
Files changed (193) hide show
  1. sentry_sdk/__init__.py +48 -6
  2. sentry_sdk/_compat.py +64 -56
  3. sentry_sdk/_init_implementation.py +84 -0
  4. sentry_sdk/_log_batcher.py +172 -0
  5. sentry_sdk/_lru_cache.py +47 -0
  6. sentry_sdk/_metrics_batcher.py +167 -0
  7. sentry_sdk/_queue.py +81 -19
  8. sentry_sdk/_types.py +311 -11
  9. sentry_sdk/_werkzeug.py +98 -0
  10. sentry_sdk/ai/__init__.py +7 -0
  11. sentry_sdk/ai/monitoring.py +137 -0
  12. sentry_sdk/ai/utils.py +144 -0
  13. sentry_sdk/api.py +409 -67
  14. sentry_sdk/attachments.py +75 -0
  15. sentry_sdk/client.py +849 -103
  16. sentry_sdk/consts.py +1389 -34
  17. sentry_sdk/crons/__init__.py +10 -0
  18. sentry_sdk/crons/api.py +62 -0
  19. sentry_sdk/crons/consts.py +4 -0
  20. sentry_sdk/crons/decorator.py +135 -0
  21. sentry_sdk/debug.py +12 -15
  22. sentry_sdk/envelope.py +112 -61
  23. sentry_sdk/feature_flags.py +71 -0
  24. sentry_sdk/hub.py +442 -386
  25. sentry_sdk/integrations/__init__.py +228 -58
  26. sentry_sdk/integrations/_asgi_common.py +108 -0
  27. sentry_sdk/integrations/_wsgi_common.py +131 -40
  28. sentry_sdk/integrations/aiohttp.py +221 -72
  29. sentry_sdk/integrations/anthropic.py +439 -0
  30. sentry_sdk/integrations/argv.py +4 -6
  31. sentry_sdk/integrations/ariadne.py +161 -0
  32. sentry_sdk/integrations/arq.py +247 -0
  33. sentry_sdk/integrations/asgi.py +237 -135
  34. sentry_sdk/integrations/asyncio.py +144 -0
  35. sentry_sdk/integrations/asyncpg.py +208 -0
  36. sentry_sdk/integrations/atexit.py +13 -18
  37. sentry_sdk/integrations/aws_lambda.py +233 -80
  38. sentry_sdk/integrations/beam.py +27 -35
  39. sentry_sdk/integrations/boto3.py +137 -0
  40. sentry_sdk/integrations/bottle.py +91 -69
  41. sentry_sdk/integrations/celery/__init__.py +529 -0
  42. sentry_sdk/integrations/celery/beat.py +293 -0
  43. sentry_sdk/integrations/celery/utils.py +43 -0
  44. sentry_sdk/integrations/chalice.py +35 -28
  45. sentry_sdk/integrations/clickhouse_driver.py +177 -0
  46. sentry_sdk/integrations/cloud_resource_context.py +280 -0
  47. sentry_sdk/integrations/cohere.py +274 -0
  48. sentry_sdk/integrations/dedupe.py +32 -8
  49. sentry_sdk/integrations/django/__init__.py +343 -89
  50. sentry_sdk/integrations/django/asgi.py +201 -22
  51. sentry_sdk/integrations/django/caching.py +204 -0
  52. sentry_sdk/integrations/django/middleware.py +80 -32
  53. sentry_sdk/integrations/django/signals_handlers.py +91 -0
  54. sentry_sdk/integrations/django/templates.py +69 -2
  55. sentry_sdk/integrations/django/transactions.py +39 -14
  56. sentry_sdk/integrations/django/views.py +69 -16
  57. sentry_sdk/integrations/dramatiq.py +226 -0
  58. sentry_sdk/integrations/excepthook.py +19 -13
  59. sentry_sdk/integrations/executing.py +5 -6
  60. sentry_sdk/integrations/falcon.py +128 -65
  61. sentry_sdk/integrations/fastapi.py +141 -0
  62. sentry_sdk/integrations/flask.py +114 -75
  63. sentry_sdk/integrations/gcp.py +67 -36
  64. sentry_sdk/integrations/gnu_backtrace.py +14 -22
  65. sentry_sdk/integrations/google_genai/__init__.py +301 -0
  66. sentry_sdk/integrations/google_genai/consts.py +16 -0
  67. sentry_sdk/integrations/google_genai/streaming.py +155 -0
  68. sentry_sdk/integrations/google_genai/utils.py +576 -0
  69. sentry_sdk/integrations/gql.py +162 -0
  70. sentry_sdk/integrations/graphene.py +151 -0
  71. sentry_sdk/integrations/grpc/__init__.py +168 -0
  72. sentry_sdk/integrations/grpc/aio/__init__.py +7 -0
  73. sentry_sdk/integrations/grpc/aio/client.py +95 -0
  74. sentry_sdk/integrations/grpc/aio/server.py +100 -0
  75. sentry_sdk/integrations/grpc/client.py +91 -0
  76. sentry_sdk/integrations/grpc/consts.py +1 -0
  77. sentry_sdk/integrations/grpc/server.py +66 -0
  78. sentry_sdk/integrations/httpx.py +178 -0
  79. sentry_sdk/integrations/huey.py +174 -0
  80. sentry_sdk/integrations/huggingface_hub.py +378 -0
  81. sentry_sdk/integrations/langchain.py +1132 -0
  82. sentry_sdk/integrations/langgraph.py +337 -0
  83. sentry_sdk/integrations/launchdarkly.py +61 -0
  84. sentry_sdk/integrations/litellm.py +287 -0
  85. sentry_sdk/integrations/litestar.py +315 -0
  86. sentry_sdk/integrations/logging.py +261 -85
  87. sentry_sdk/integrations/loguru.py +213 -0
  88. sentry_sdk/integrations/mcp.py +566 -0
  89. sentry_sdk/integrations/modules.py +6 -33
  90. sentry_sdk/integrations/openai.py +725 -0
  91. sentry_sdk/integrations/openai_agents/__init__.py +61 -0
  92. sentry_sdk/integrations/openai_agents/consts.py +1 -0
  93. sentry_sdk/integrations/openai_agents/patches/__init__.py +5 -0
  94. sentry_sdk/integrations/openai_agents/patches/agent_run.py +140 -0
  95. sentry_sdk/integrations/openai_agents/patches/error_tracing.py +77 -0
  96. sentry_sdk/integrations/openai_agents/patches/models.py +50 -0
  97. sentry_sdk/integrations/openai_agents/patches/runner.py +45 -0
  98. sentry_sdk/integrations/openai_agents/patches/tools.py +77 -0
  99. sentry_sdk/integrations/openai_agents/spans/__init__.py +5 -0
  100. sentry_sdk/integrations/openai_agents/spans/agent_workflow.py +21 -0
  101. sentry_sdk/integrations/openai_agents/spans/ai_client.py +42 -0
  102. sentry_sdk/integrations/openai_agents/spans/execute_tool.py +48 -0
  103. sentry_sdk/integrations/openai_agents/spans/handoff.py +19 -0
  104. sentry_sdk/integrations/openai_agents/spans/invoke_agent.py +86 -0
  105. sentry_sdk/integrations/openai_agents/utils.py +199 -0
  106. sentry_sdk/integrations/openfeature.py +35 -0
  107. sentry_sdk/integrations/opentelemetry/__init__.py +7 -0
  108. sentry_sdk/integrations/opentelemetry/consts.py +5 -0
  109. sentry_sdk/integrations/opentelemetry/integration.py +58 -0
  110. sentry_sdk/integrations/opentelemetry/propagator.py +117 -0
  111. sentry_sdk/integrations/opentelemetry/span_processor.py +391 -0
  112. sentry_sdk/integrations/otlp.py +82 -0
  113. sentry_sdk/integrations/pure_eval.py +20 -11
  114. sentry_sdk/integrations/pydantic_ai/__init__.py +47 -0
  115. sentry_sdk/integrations/pydantic_ai/consts.py +1 -0
  116. sentry_sdk/integrations/pydantic_ai/patches/__init__.py +4 -0
  117. sentry_sdk/integrations/pydantic_ai/patches/agent_run.py +215 -0
  118. sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py +110 -0
  119. sentry_sdk/integrations/pydantic_ai/patches/model_request.py +40 -0
  120. sentry_sdk/integrations/pydantic_ai/patches/tools.py +98 -0
  121. sentry_sdk/integrations/pydantic_ai/spans/__init__.py +3 -0
  122. sentry_sdk/integrations/pydantic_ai/spans/ai_client.py +246 -0
  123. sentry_sdk/integrations/pydantic_ai/spans/execute_tool.py +49 -0
  124. sentry_sdk/integrations/pydantic_ai/spans/invoke_agent.py +112 -0
  125. sentry_sdk/integrations/pydantic_ai/utils.py +223 -0
  126. sentry_sdk/integrations/pymongo.py +214 -0
  127. sentry_sdk/integrations/pyramid.py +71 -60
  128. sentry_sdk/integrations/quart.py +237 -0
  129. sentry_sdk/integrations/ray.py +165 -0
  130. sentry_sdk/integrations/redis/__init__.py +48 -0
  131. sentry_sdk/integrations/redis/_async_common.py +116 -0
  132. sentry_sdk/integrations/redis/_sync_common.py +119 -0
  133. sentry_sdk/integrations/redis/consts.py +19 -0
  134. sentry_sdk/integrations/redis/modules/__init__.py +0 -0
  135. sentry_sdk/integrations/redis/modules/caches.py +118 -0
  136. sentry_sdk/integrations/redis/modules/queries.py +65 -0
  137. sentry_sdk/integrations/redis/rb.py +32 -0
  138. sentry_sdk/integrations/redis/redis.py +69 -0
  139. sentry_sdk/integrations/redis/redis_cluster.py +107 -0
  140. sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +50 -0
  141. sentry_sdk/integrations/redis/utils.py +148 -0
  142. sentry_sdk/integrations/rq.py +62 -52
  143. sentry_sdk/integrations/rust_tracing.py +284 -0
  144. sentry_sdk/integrations/sanic.py +248 -114
  145. sentry_sdk/integrations/serverless.py +13 -22
  146. sentry_sdk/integrations/socket.py +96 -0
  147. sentry_sdk/integrations/spark/spark_driver.py +115 -62
  148. sentry_sdk/integrations/spark/spark_worker.py +42 -50
  149. sentry_sdk/integrations/sqlalchemy.py +82 -37
  150. sentry_sdk/integrations/starlette.py +737 -0
  151. sentry_sdk/integrations/starlite.py +292 -0
  152. sentry_sdk/integrations/statsig.py +37 -0
  153. sentry_sdk/integrations/stdlib.py +100 -58
  154. sentry_sdk/integrations/strawberry.py +394 -0
  155. sentry_sdk/integrations/sys_exit.py +70 -0
  156. sentry_sdk/integrations/threading.py +142 -38
  157. sentry_sdk/integrations/tornado.py +68 -53
  158. sentry_sdk/integrations/trytond.py +15 -20
  159. sentry_sdk/integrations/typer.py +60 -0
  160. sentry_sdk/integrations/unleash.py +33 -0
  161. sentry_sdk/integrations/unraisablehook.py +53 -0
  162. sentry_sdk/integrations/wsgi.py +126 -125
  163. sentry_sdk/logger.py +96 -0
  164. sentry_sdk/metrics.py +81 -0
  165. sentry_sdk/monitor.py +120 -0
  166. sentry_sdk/profiler/__init__.py +49 -0
  167. sentry_sdk/profiler/continuous_profiler.py +730 -0
  168. sentry_sdk/profiler/transaction_profiler.py +839 -0
  169. sentry_sdk/profiler/utils.py +195 -0
  170. sentry_sdk/scope.py +1542 -112
  171. sentry_sdk/scrubber.py +177 -0
  172. sentry_sdk/serializer.py +152 -210
  173. sentry_sdk/session.py +177 -0
  174. sentry_sdk/sessions.py +202 -179
  175. sentry_sdk/spotlight.py +242 -0
  176. sentry_sdk/tracing.py +1202 -294
  177. sentry_sdk/tracing_utils.py +1236 -0
  178. sentry_sdk/transport.py +693 -189
  179. sentry_sdk/types.py +52 -0
  180. sentry_sdk/utils.py +1395 -228
  181. sentry_sdk/worker.py +30 -17
  182. sentry_sdk-2.46.0.dist-info/METADATA +268 -0
  183. sentry_sdk-2.46.0.dist-info/RECORD +189 -0
  184. {sentry_sdk-0.18.0.dist-info → sentry_sdk-2.46.0.dist-info}/WHEEL +1 -1
  185. sentry_sdk-2.46.0.dist-info/entry_points.txt +2 -0
  186. sentry_sdk-2.46.0.dist-info/licenses/LICENSE +21 -0
  187. sentry_sdk/_functools.py +0 -66
  188. sentry_sdk/integrations/celery.py +0 -275
  189. sentry_sdk/integrations/redis.py +0 -103
  190. sentry_sdk-0.18.0.dist-info/LICENSE +0 -9
  191. sentry_sdk-0.18.0.dist-info/METADATA +0 -66
  192. sentry_sdk-0.18.0.dist-info/RECORD +0 -65
  193. {sentry_sdk-0.18.0.dist-info → sentry_sdk-2.46.0.dist-info}/top_level.txt +0 -0
@@ -1,26 +1,27 @@
1
- from __future__ import absolute_import
2
-
3
- import weakref
4
-
5
- from sentry_sdk.hub import Hub, _should_send_default_pii
6
- from sentry_sdk.utils import capture_internal_exceptions, event_from_exception
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.integrations._wsgi_common import RequestExtractor
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 sentry_sdk._types import MYPY
17
+ from typing import TYPE_CHECKING
12
18
 
13
- if MYPY:
14
- from sentry_sdk.integrations.wsgi import _ScopedResponse
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 ( # type: ignore
33
- Request,
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 = None
56
+ transaction_style = ""
54
57
 
55
- def __init__(self, transaction_style="endpoint"):
56
- # type: (str) -> None
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
- version = tuple(map(int, FLASK_VERSION.split(".")[:3]))
69
- except (ValueError, TypeError):
70
- raise DidNotEnable("Unparsable Flask version: {}".format(FLASK_VERSION))
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
- if version < (0, 10):
73
- raise DidNotEnable("Flask 0.10 or newer is required.")
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 Hub.current.get_integration(FlaskIntegration) is None:
98
+ if sentry_sdk.get_client().get_integration(FlaskIntegration) is None:
83
99
  return old_app(self, environ, start_response)
84
100
 
85
- return SentryWsgiMiddleware(lambda *a, **kw: old_app(self, *a, **kw))(
86
- environ, start_response
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 # type: ignore
114
+ Flask.__call__ = sentry_patched_wsgi_app
90
115
 
91
116
 
92
- def _request_started(sender, **kwargs):
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
- hub = Hub.current
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
- app = _app_ctx_stack.top.app
100
- with hub.configure_scope() as scope:
101
- request = _request_ctx_stack.top.request
149
+ request = flask_request._get_current_object()
102
150
 
103
- # Rely on WSGI middleware to start a trace
104
- try:
105
- if integration.transaction_style == "endpoint":
106
- scope.transaction = request.url_rule.endpoint
107
- elif integration.transaction_style == "url":
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
- weak_request = weakref.ref(request)
113
- evt_processor = _make_request_event_processor(
114
- app, weak_request, integration # type: ignore
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: () -> ImmutableTypeConversionDict[Any, Any]
126
- return self.request.cookies
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, weak_request, integration):
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: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]
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 _should_send_default_pii():
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=client.options,
228
+ client_options=sentry_sdk.get_client().options,
189
229
  mechanism={"type": "flask", "handled": False},
190
230
  )
191
231
 
192
- hub.capture_event(event, hint=hint)
232
+ sentry_sdk.capture_event(event, hint=hint)
193
233
 
194
234
 
195
235
  def _add_user_to_event(event):
196
- # type: (Dict[str, Any]) -> None
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
@@ -1,27 +1,32 @@
1
- from datetime import datetime, timedelta
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
- from sentry_sdk.hub import Hub, _should_send_default_pii
6
- from sentry_sdk.tracing import Transaction
7
- from sentry_sdk._compat import reraise
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 sentry_sdk._types import MYPY
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 MYPY:
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
- def sentry_func(functionhandler, event, *args, **kwargs):
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
- hub = Hub.current
41
- integration = hub.get_integration(GcpIntegration)
47
+ integration = client.get_integration(GcpIntegration)
42
48
  if integration is None:
43
- return func(functionhandler, event, *args, **kwargs)
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, event, *args, **kwargs)
56
+ return func(functionhandler, gcp_event, *args, **kwargs)
54
57
 
55
58
  configured_time = int(configured_time)
56
59
 
57
- initial_time = datetime.utcnow()
60
+ initial_time = datetime.now(timezone.utc)
58
61
 
59
- with hub.push_scope() as scope:
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(event, configured_time, initial_time)
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(waiting_time, configured_time)
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(event, "headers"):
79
- headers = event.headers
80
- transaction = Transaction.continue_from_headers(
81
- headers, op="serverless.function", name=environ.get("FUNCTION_NAME", "")
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
- with hub.start_transaction(transaction):
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, event, *args, **kwargs)
113
+ return func(functionhandler, gcp_event, *args, **kwargs)
86
114
  except Exception:
87
115
  exc_info = sys.exc_info()
88
- event, hint = event_from_exception(
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
- hub.capture_event(event, hint=hint)
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
- hub.flush()
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 # type: ignore
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.utcnow()
164
+ final_time = datetime.now(timezone.utc)
134
165
  time_diff = final_time - initial_time
135
166
 
136
- execution_duration_in_millis = time_diff.microseconds / MILLIS_TO_SECONDS
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 _should_send_default_pii():
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("", {"rem": [["!raw", "x", 0, 0]]})
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
- from sentry_sdk.hub import Hub
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 sentry_sdk._types import MYPY
8
+ from typing import TYPE_CHECKING
9
9
 
10
- if MYPY:
10
+ if TYPE_CHECKING:
11
11
  from typing import Any
12
- from typing import Dict
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
- MODULE_RE=MODULE_RE, HEXVAL_RE=HEXVAL_RE, TYPE_RE=TYPE_RE
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: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]
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: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]
52
- if Hub.current.get_integration(GnuBacktraceIntegration) is None:
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)