sentry-sdk 3.0.0a5__py2.py3-none-any.whl → 3.0.0a7__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_implementation.py +5 -0
- sentry_sdk/ai/utils.py +7 -8
- sentry_sdk/api.py +13 -2
- sentry_sdk/client.py +93 -17
- sentry_sdk/consts.py +17 -7
- sentry_sdk/crons/api.py +5 -0
- sentry_sdk/integrations/anthropic.py +133 -73
- sentry_sdk/integrations/asgi.py +10 -9
- sentry_sdk/integrations/asyncio.py +85 -20
- sentry_sdk/integrations/clickhouse_driver.py +55 -28
- sentry_sdk/integrations/fastapi.py +1 -7
- sentry_sdk/integrations/gnu_backtrace.py +6 -3
- sentry_sdk/integrations/langchain.py +462 -218
- sentry_sdk/integrations/litestar.py +1 -1
- sentry_sdk/integrations/openai_agents/patches/agent_run.py +0 -2
- sentry_sdk/integrations/openai_agents/patches/runner.py +18 -15
- sentry_sdk/integrations/quart.py +1 -1
- sentry_sdk/integrations/starlette.py +1 -5
- sentry_sdk/integrations/starlite.py +2 -2
- sentry_sdk/integrations/threading.py +1 -1
- sentry_sdk/scope.py +11 -11
- sentry_sdk/spotlight.py +1 -162
- sentry_sdk/tracing.py +94 -17
- sentry_sdk/tracing_utils.py +330 -33
- sentry_sdk/transport.py +363 -63
- sentry_sdk/utils.py +23 -5
- sentry_sdk/worker.py +197 -3
- {sentry_sdk-3.0.0a5.dist-info → sentry_sdk-3.0.0a7.dist-info}/METADATA +3 -1
- {sentry_sdk-3.0.0a5.dist-info → sentry_sdk-3.0.0a7.dist-info}/RECORD +33 -33
- {sentry_sdk-3.0.0a5.dist-info → sentry_sdk-3.0.0a7.dist-info}/WHEEL +0 -0
- {sentry_sdk-3.0.0a5.dist-info → sentry_sdk-3.0.0a7.dist-info}/entry_points.txt +0 -0
- {sentry_sdk-3.0.0a5.dist-info → sentry_sdk-3.0.0a7.dist-info}/licenses/LICENSE +0 -0
- {sentry_sdk-3.0.0a5.dist-info → sentry_sdk-3.0.0a7.dist-info}/top_level.txt +0 -0
|
@@ -85,6 +85,7 @@ class SentryLitestarASGIMiddleware(SentryAsgiMiddleware):
|
|
|
85
85
|
transaction_style="endpoint",
|
|
86
86
|
mechanism_type="asgi",
|
|
87
87
|
span_origin=span_origin,
|
|
88
|
+
asgi_version=3,
|
|
88
89
|
)
|
|
89
90
|
|
|
90
91
|
def _capture_request_exception(self, exc: Exception) -> None:
|
|
@@ -113,7 +114,6 @@ def patch_app_init() -> None:
|
|
|
113
114
|
*(kwargs.get("after_exception") or []),
|
|
114
115
|
]
|
|
115
116
|
|
|
116
|
-
SentryLitestarASGIMiddleware.__call__ = SentryLitestarASGIMiddleware._run_asgi3 # type: ignore
|
|
117
117
|
middleware = kwargs.get("middleware") or []
|
|
118
118
|
kwargs["middleware"] = [SentryLitestarASGIMiddleware, *middleware]
|
|
119
119
|
old__init__(self, *args, **kwargs)
|
|
@@ -3,7 +3,6 @@ from __future__ import annotations
|
|
|
3
3
|
from functools import wraps
|
|
4
4
|
|
|
5
5
|
from sentry_sdk.integrations import DidNotEnable
|
|
6
|
-
|
|
7
6
|
from ..spans import invoke_agent_span, update_invoke_agent_span, handoff_span
|
|
8
7
|
|
|
9
8
|
from typing import TYPE_CHECKING
|
|
@@ -11,7 +10,6 @@ from typing import TYPE_CHECKING
|
|
|
11
10
|
if TYPE_CHECKING:
|
|
12
11
|
from typing import Any, Optional
|
|
13
12
|
|
|
14
|
-
|
|
15
13
|
try:
|
|
16
14
|
import agents
|
|
17
15
|
except ImportError:
|
|
@@ -23,20 +23,23 @@ def _create_run_wrapper(original_func: Callable[..., Any]) -> Callable[..., Any]
|
|
|
23
23
|
|
|
24
24
|
@wraps(original_func)
|
|
25
25
|
async def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
span
|
|
39
|
-
|
|
40
|
-
|
|
26
|
+
# Isolate each workflow so that when agents are run in asyncio tasks they
|
|
27
|
+
# don't touch each other's scopes
|
|
28
|
+
with sentry_sdk.isolation_scope():
|
|
29
|
+
agent = args[0]
|
|
30
|
+
with agent_workflow_span(agent):
|
|
31
|
+
result = None
|
|
32
|
+
try:
|
|
33
|
+
result = await original_func(*args, **kwargs)
|
|
34
|
+
return result
|
|
35
|
+
except Exception as exc:
|
|
36
|
+
_capture_exception(exc)
|
|
37
|
+
|
|
38
|
+
# It could be that there is a "invoke agent" span still open
|
|
39
|
+
current_span = sentry_sdk.get_current_span()
|
|
40
|
+
if current_span is not None and current_span.timestamp is None:
|
|
41
|
+
current_span.__exit__(None, None, None)
|
|
42
|
+
|
|
43
|
+
raise exc from None
|
|
41
44
|
|
|
42
45
|
return wrapper
|
sentry_sdk/integrations/quart.py
CHANGED
|
@@ -94,8 +94,8 @@ def patch_asgi_app() -> None:
|
|
|
94
94
|
middleware = SentryAsgiMiddleware(
|
|
95
95
|
lambda *a, **kw: old_app(self, *a, **kw),
|
|
96
96
|
span_origin=QuartIntegration.origin,
|
|
97
|
+
asgi_version=3,
|
|
97
98
|
)
|
|
98
|
-
middleware.__call__ = middleware._run_asgi3
|
|
99
99
|
return await middleware(scope, receive, send)
|
|
100
100
|
|
|
101
101
|
Quart.__call__ = sentry_patched_asgi_app
|
|
@@ -24,7 +24,6 @@ from sentry_sdk.utils import (
|
|
|
24
24
|
capture_internal_exceptions,
|
|
25
25
|
ensure_integration_enabled,
|
|
26
26
|
event_from_exception,
|
|
27
|
-
logger,
|
|
28
27
|
parse_version,
|
|
29
28
|
transaction_from_function,
|
|
30
29
|
)
|
|
@@ -388,9 +387,9 @@ def patch_asgi_app() -> None:
|
|
|
388
387
|
if integration
|
|
389
388
|
else DEFAULT_HTTP_METHODS_TO_CAPTURE
|
|
390
389
|
),
|
|
390
|
+
asgi_version=3,
|
|
391
391
|
)
|
|
392
392
|
|
|
393
|
-
middleware.__call__ = middleware._run_asgi3
|
|
394
393
|
return await middleware(scope, receive, send)
|
|
395
394
|
|
|
396
395
|
Starlette.__call__ = _sentry_patched_asgi_app
|
|
@@ -698,9 +697,6 @@ def _set_transaction_name_and_source(
|
|
|
698
697
|
source = TransactionSource.ROUTE
|
|
699
698
|
|
|
700
699
|
scope.set_transaction_name(name, source=source)
|
|
701
|
-
logger.debug(
|
|
702
|
-
"[Starlette] Set transaction name and source on scope: %s / %s", name, source
|
|
703
|
-
)
|
|
704
700
|
|
|
705
701
|
|
|
706
702
|
def _get_transaction_from_middleware(
|
|
@@ -17,7 +17,7 @@ try:
|
|
|
17
17
|
from starlite.plugins.base import get_plugin_for_value # type: ignore
|
|
18
18
|
from starlite.routes.http import HTTPRoute # type: ignore
|
|
19
19
|
from starlite.utils import ConnectionDataExtractor, is_async_callable, Ref # type: ignore
|
|
20
|
-
from pydantic import BaseModel
|
|
20
|
+
from pydantic import BaseModel # type: ignore
|
|
21
21
|
except ImportError:
|
|
22
22
|
raise DidNotEnable("Starlite is not installed")
|
|
23
23
|
|
|
@@ -65,6 +65,7 @@ class SentryStarliteASGIMiddleware(SentryAsgiMiddleware):
|
|
|
65
65
|
transaction_style="endpoint",
|
|
66
66
|
mechanism_type="asgi",
|
|
67
67
|
span_origin=span_origin,
|
|
68
|
+
asgi_version=3,
|
|
68
69
|
)
|
|
69
70
|
|
|
70
71
|
|
|
@@ -92,7 +93,6 @@ def patch_app_init() -> None:
|
|
|
92
93
|
]
|
|
93
94
|
)
|
|
94
95
|
|
|
95
|
-
SentryStarliteASGIMiddleware.__call__ = SentryStarliteASGIMiddleware._run_asgi3 # type: ignore
|
|
96
96
|
middleware = kwargs.get("middleware") or []
|
|
97
97
|
kwargs["middleware"] = [SentryStarliteASGIMiddleware, *middleware]
|
|
98
98
|
old__init__(self, *args, **kwargs)
|
|
@@ -38,7 +38,7 @@ class ThreadingIntegration(Integration):
|
|
|
38
38
|
|
|
39
39
|
try:
|
|
40
40
|
from django import VERSION as django_version # noqa: N811
|
|
41
|
-
import channels # type: ignore
|
|
41
|
+
import channels # type: ignore
|
|
42
42
|
|
|
43
43
|
channels_version = channels.__version__
|
|
44
44
|
except ImportError:
|
sentry_sdk/scope.py
CHANGED
|
@@ -61,7 +61,7 @@ if TYPE_CHECKING:
|
|
|
61
61
|
Self,
|
|
62
62
|
)
|
|
63
63
|
|
|
64
|
-
from collections.abc import Mapping
|
|
64
|
+
from collections.abc import Mapping
|
|
65
65
|
|
|
66
66
|
import sentry_sdk
|
|
67
67
|
from sentry_sdk._types import (
|
|
@@ -201,24 +201,24 @@ class Scope:
|
|
|
201
201
|
rv._name = self._name
|
|
202
202
|
rv._fingerprint = self._fingerprint
|
|
203
203
|
rv._transaction = self._transaction
|
|
204
|
-
rv._transaction_info =
|
|
204
|
+
rv._transaction_info = self._transaction_info.copy()
|
|
205
205
|
rv._user = self._user
|
|
206
206
|
|
|
207
|
-
rv._tags =
|
|
208
|
-
rv._contexts =
|
|
209
|
-
rv._extras =
|
|
207
|
+
rv._tags = self._tags.copy()
|
|
208
|
+
rv._contexts = self._contexts.copy()
|
|
209
|
+
rv._extras = self._extras.copy()
|
|
210
210
|
|
|
211
211
|
rv._breadcrumbs = copy(self._breadcrumbs)
|
|
212
|
-
rv._n_breadcrumbs_truncated =
|
|
213
|
-
rv._event_processors =
|
|
214
|
-
rv._error_processors =
|
|
212
|
+
rv._n_breadcrumbs_truncated = self._n_breadcrumbs_truncated
|
|
213
|
+
rv._event_processors = self._event_processors.copy()
|
|
214
|
+
rv._error_processors = self._error_processors.copy()
|
|
215
215
|
rv._propagation_context = self._propagation_context
|
|
216
216
|
|
|
217
217
|
rv._should_capture = self._should_capture
|
|
218
218
|
rv._span = self._span
|
|
219
219
|
rv._session = self._session
|
|
220
220
|
rv._force_auto_session_tracking = self._force_auto_session_tracking
|
|
221
|
-
rv._attachments =
|
|
221
|
+
rv._attachments = self._attachments.copy()
|
|
222
222
|
|
|
223
223
|
rv._profile = self._profile
|
|
224
224
|
|
|
@@ -627,12 +627,12 @@ class Scope:
|
|
|
627
627
|
self._level: Optional[LogLevelStr] = None
|
|
628
628
|
self._fingerprint: Optional[List[str]] = None
|
|
629
629
|
self._transaction: Optional[str] = None
|
|
630
|
-
self._transaction_info:
|
|
630
|
+
self._transaction_info: Dict[str, str] = {}
|
|
631
631
|
self._user: Optional[Dict[str, Any]] = None
|
|
632
632
|
|
|
633
633
|
self._tags: Dict[str, Any] = {}
|
|
634
634
|
self._contexts: Dict[str, Dict[str, Any]] = {}
|
|
635
|
-
self._extras:
|
|
635
|
+
self._extras: Dict[str, Any] = {}
|
|
636
636
|
self._attachments: List[Attachment] = []
|
|
637
637
|
|
|
638
638
|
self.clear_breadcrumbs()
|
sentry_sdk/spotlight.py
CHANGED
|
@@ -1,24 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import io
|
|
3
3
|
import logging
|
|
4
|
-
import os
|
|
5
|
-
import urllib.parse
|
|
6
|
-
import urllib.request
|
|
7
|
-
import urllib.error
|
|
8
4
|
import urllib3
|
|
9
5
|
import sys
|
|
10
6
|
|
|
11
|
-
from itertools import chain, product
|
|
12
|
-
|
|
13
7
|
from typing import TYPE_CHECKING
|
|
14
8
|
|
|
15
9
|
if TYPE_CHECKING:
|
|
16
|
-
from typing import Any,
|
|
10
|
+
from typing import Any, Dict, Optional
|
|
17
11
|
|
|
18
12
|
from sentry_sdk.utils import (
|
|
19
13
|
logger as sentry_logger,
|
|
20
|
-
env_to_bool,
|
|
21
|
-
capture_internal_exceptions,
|
|
22
14
|
)
|
|
23
15
|
from sentry_sdk.envelope import Envelope
|
|
24
16
|
|
|
@@ -63,145 +55,6 @@ class SpotlightClient:
|
|
|
63
55
|
# to avoid overflowing the variable if Spotlight never becomes reachable
|
|
64
56
|
|
|
65
57
|
|
|
66
|
-
try:
|
|
67
|
-
from django.utils.deprecation import MiddlewareMixin
|
|
68
|
-
from django.http import HttpResponseServerError, HttpResponse, HttpRequest
|
|
69
|
-
from django.conf import settings
|
|
70
|
-
|
|
71
|
-
SPOTLIGHT_JS_ENTRY_PATH = "/assets/main.js"
|
|
72
|
-
SPOTLIGHT_JS_SNIPPET_PATTERN = (
|
|
73
|
-
"<script>window.__spotlight = {{ initOptions: {{ sidecarUrl: '{spotlight_url}', fullPage: false }} }};</script>\n"
|
|
74
|
-
'<script type="module" crossorigin src="{spotlight_js_url}"></script>\n'
|
|
75
|
-
)
|
|
76
|
-
SPOTLIGHT_ERROR_PAGE_SNIPPET = (
|
|
77
|
-
'<html><base href="{spotlight_url}">\n'
|
|
78
|
-
'<script>window.__spotlight = {{ initOptions: {{ fullPage: true, startFrom: "/errors/{event_id}" }}}};</script>\n'
|
|
79
|
-
)
|
|
80
|
-
CHARSET_PREFIX = "charset="
|
|
81
|
-
BODY_TAG_NAME = "body"
|
|
82
|
-
BODY_CLOSE_TAG_POSSIBILITIES = tuple(
|
|
83
|
-
"</{}>".format("".join(chars))
|
|
84
|
-
for chars in product(*zip(BODY_TAG_NAME.upper(), BODY_TAG_NAME.lower()))
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
class SpotlightMiddleware(MiddlewareMixin): # type: ignore[misc]
|
|
88
|
-
_spotlight_script: Optional[str] = None
|
|
89
|
-
_spotlight_url: Optional[str] = None
|
|
90
|
-
|
|
91
|
-
def __init__(self, get_response: Callable[..., HttpResponse]) -> None:
|
|
92
|
-
super().__init__(get_response)
|
|
93
|
-
|
|
94
|
-
import sentry_sdk.api
|
|
95
|
-
|
|
96
|
-
self.sentry_sdk = sentry_sdk.api
|
|
97
|
-
|
|
98
|
-
spotlight_client = self.sentry_sdk.get_client().spotlight
|
|
99
|
-
if spotlight_client is None:
|
|
100
|
-
sentry_logger.warning(
|
|
101
|
-
"Cannot find Spotlight client from SpotlightMiddleware, disabling the middleware."
|
|
102
|
-
)
|
|
103
|
-
return None
|
|
104
|
-
# Spotlight URL has a trailing `/stream` part at the end so split it off
|
|
105
|
-
self._spotlight_url = urllib.parse.urljoin(spotlight_client.url, "../")
|
|
106
|
-
|
|
107
|
-
@property
|
|
108
|
-
def spotlight_script(self) -> Optional[str]:
|
|
109
|
-
if self._spotlight_url is not None and self._spotlight_script is None:
|
|
110
|
-
try:
|
|
111
|
-
spotlight_js_url = urllib.parse.urljoin(
|
|
112
|
-
self._spotlight_url, SPOTLIGHT_JS_ENTRY_PATH
|
|
113
|
-
)
|
|
114
|
-
req = urllib.request.Request(
|
|
115
|
-
spotlight_js_url,
|
|
116
|
-
method="HEAD",
|
|
117
|
-
)
|
|
118
|
-
urllib.request.urlopen(req)
|
|
119
|
-
self._spotlight_script = SPOTLIGHT_JS_SNIPPET_PATTERN.format(
|
|
120
|
-
spotlight_url=self._spotlight_url,
|
|
121
|
-
spotlight_js_url=spotlight_js_url,
|
|
122
|
-
)
|
|
123
|
-
except urllib.error.URLError as err:
|
|
124
|
-
sentry_logger.debug(
|
|
125
|
-
"Cannot get Spotlight JS to inject at %s. SpotlightMiddleware will not be very useful.",
|
|
126
|
-
spotlight_js_url,
|
|
127
|
-
exc_info=err,
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
return self._spotlight_script
|
|
131
|
-
|
|
132
|
-
def process_response(
|
|
133
|
-
self, _request: HttpRequest, response: HttpResponse
|
|
134
|
-
) -> Optional[HttpResponse]:
|
|
135
|
-
content_type_header = tuple(
|
|
136
|
-
p.strip()
|
|
137
|
-
for p in response.headers.get("Content-Type", "").lower().split(";")
|
|
138
|
-
)
|
|
139
|
-
content_type = content_type_header[0]
|
|
140
|
-
if len(content_type_header) > 1 and content_type_header[1].startswith(
|
|
141
|
-
CHARSET_PREFIX
|
|
142
|
-
):
|
|
143
|
-
encoding = content_type_header[1][len(CHARSET_PREFIX) :]
|
|
144
|
-
else:
|
|
145
|
-
encoding = "utf-8"
|
|
146
|
-
|
|
147
|
-
if (
|
|
148
|
-
self.spotlight_script is not None
|
|
149
|
-
and not response.streaming
|
|
150
|
-
and content_type == "text/html"
|
|
151
|
-
):
|
|
152
|
-
content_length = len(response.content)
|
|
153
|
-
injection = self.spotlight_script.encode(encoding)
|
|
154
|
-
injection_site = next(
|
|
155
|
-
(
|
|
156
|
-
idx
|
|
157
|
-
for idx in (
|
|
158
|
-
response.content.rfind(body_variant.encode(encoding))
|
|
159
|
-
for body_variant in BODY_CLOSE_TAG_POSSIBILITIES
|
|
160
|
-
)
|
|
161
|
-
if idx > -1
|
|
162
|
-
),
|
|
163
|
-
content_length,
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
# This approach works even when we don't have a `</body>` tag
|
|
167
|
-
response.content = (
|
|
168
|
-
response.content[:injection_site]
|
|
169
|
-
+ injection
|
|
170
|
-
+ response.content[injection_site:]
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
if response.has_header("Content-Length"):
|
|
174
|
-
response.headers["Content-Length"] = content_length + len(injection)
|
|
175
|
-
|
|
176
|
-
return response
|
|
177
|
-
|
|
178
|
-
def process_exception(
|
|
179
|
-
self, _request: HttpRequest, exception: Exception
|
|
180
|
-
) -> Optional[HttpResponseServerError]:
|
|
181
|
-
if not settings.DEBUG or not self._spotlight_url:
|
|
182
|
-
return None
|
|
183
|
-
|
|
184
|
-
try:
|
|
185
|
-
spotlight = (
|
|
186
|
-
urllib.request.urlopen(self._spotlight_url).read().decode("utf-8")
|
|
187
|
-
)
|
|
188
|
-
except urllib.error.URLError:
|
|
189
|
-
return None
|
|
190
|
-
else:
|
|
191
|
-
event_id = self.sentry_sdk.capture_exception(exception)
|
|
192
|
-
return HttpResponseServerError(
|
|
193
|
-
spotlight.replace(
|
|
194
|
-
"<html>",
|
|
195
|
-
SPOTLIGHT_ERROR_PAGE_SNIPPET.format(
|
|
196
|
-
spotlight_url=self._spotlight_url, event_id=event_id
|
|
197
|
-
),
|
|
198
|
-
)
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
except ImportError:
|
|
202
|
-
settings = None
|
|
203
|
-
|
|
204
|
-
|
|
205
58
|
def setup_spotlight(options: Dict[str, Any]) -> Optional[SpotlightClient]:
|
|
206
59
|
_handler = logging.StreamHandler(sys.stderr)
|
|
207
60
|
_handler.setFormatter(logging.Formatter(" [spotlight] %(levelname)s: %(message)s"))
|
|
@@ -216,20 +69,6 @@ def setup_spotlight(options: Dict[str, Any]) -> Optional[SpotlightClient]:
|
|
|
216
69
|
if not isinstance(url, str):
|
|
217
70
|
return None
|
|
218
71
|
|
|
219
|
-
with capture_internal_exceptions():
|
|
220
|
-
if (
|
|
221
|
-
settings is not None
|
|
222
|
-
and settings.DEBUG
|
|
223
|
-
and env_to_bool(os.environ.get("SENTRY_SPOTLIGHT_ON_ERROR", "1"))
|
|
224
|
-
and env_to_bool(os.environ.get("SENTRY_SPOTLIGHT_MIDDLEWARE", "1"))
|
|
225
|
-
):
|
|
226
|
-
middleware = settings.MIDDLEWARE
|
|
227
|
-
if DJANGO_SPOTLIGHT_MIDDLEWARE_PATH not in middleware:
|
|
228
|
-
settings.MIDDLEWARE = type(middleware)(
|
|
229
|
-
chain(middleware, (DJANGO_SPOTLIGHT_MIDDLEWARE_PATH,))
|
|
230
|
-
)
|
|
231
|
-
logger.info("Enabled Spotlight integration for Django")
|
|
232
|
-
|
|
233
72
|
client = SpotlightClient(url)
|
|
234
73
|
logger.info("Enabled Spotlight using sidecar at %s", url)
|
|
235
74
|
|
sentry_sdk/tracing.py
CHANGED
|
@@ -23,6 +23,7 @@ from sentry_sdk.consts import (
|
|
|
23
23
|
SENTRY_TRACE_HEADER_NAME,
|
|
24
24
|
SPANSTATUS,
|
|
25
25
|
SPANDATA,
|
|
26
|
+
SPANTEMPLATE,
|
|
26
27
|
TransactionSource,
|
|
27
28
|
)
|
|
28
29
|
from sentry_sdk.opentelemetry.consts import (
|
|
@@ -278,6 +279,14 @@ class Span:
|
|
|
278
279
|
self.finish()
|
|
279
280
|
self.deactivate()
|
|
280
281
|
|
|
282
|
+
async def __aenter__(self) -> Span:
|
|
283
|
+
return self.__enter__()
|
|
284
|
+
|
|
285
|
+
async def __aexit__(
|
|
286
|
+
self, ty: Optional[Any], value: Optional[Any], tb: Optional[Any]
|
|
287
|
+
) -> None:
|
|
288
|
+
return self.__exit__(ty, value, tb)
|
|
289
|
+
|
|
281
290
|
@property
|
|
282
291
|
def description(self) -> Optional[str]:
|
|
283
292
|
return self.get_attribute(SentrySpanAttribute.DESCRIPTION)
|
|
@@ -543,39 +552,107 @@ Transaction = Span
|
|
|
543
552
|
if TYPE_CHECKING:
|
|
544
553
|
|
|
545
554
|
@overload
|
|
546
|
-
def trace(
|
|
555
|
+
def trace(
|
|
556
|
+
func: Optional[Callable[P, R]] = None,
|
|
557
|
+
*,
|
|
558
|
+
op: Optional[str] = None,
|
|
559
|
+
name: Optional[str] = None,
|
|
560
|
+
attributes: Optional[dict[str, Any]] = None,
|
|
561
|
+
template: SPANTEMPLATE = SPANTEMPLATE.DEFAULT,
|
|
562
|
+
) -> Union[Callable[P, R], Callable[[Callable[P, R]], Callable[P, R]]]:
|
|
563
|
+
# Handles: @trace() and @trace(op="custom")
|
|
547
564
|
pass
|
|
548
565
|
|
|
549
566
|
@overload
|
|
550
567
|
def trace(func: Callable[P, R]) -> Callable[P, R]:
|
|
568
|
+
# Handles: @trace
|
|
551
569
|
pass
|
|
552
570
|
|
|
553
571
|
|
|
554
572
|
def trace(
|
|
555
573
|
func: Optional[Callable[P, R]] = None,
|
|
574
|
+
*,
|
|
575
|
+
op: Optional[str] = None,
|
|
576
|
+
name: Optional[str] = None,
|
|
577
|
+
attributes: Optional[dict[str, Any]] = None,
|
|
578
|
+
template: SPANTEMPLATE = SPANTEMPLATE.DEFAULT,
|
|
556
579
|
) -> Union[Callable[P, R], Callable[[Callable[P, R]], Callable[P, R]]]:
|
|
557
580
|
"""
|
|
558
|
-
Decorator to start a child span
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
581
|
+
Decorator to start a child span around a function call.
|
|
582
|
+
|
|
583
|
+
This decorator automatically creates a new span when the decorated function
|
|
584
|
+
is called, and finishes the span when the function returns or raises an exception.
|
|
585
|
+
|
|
586
|
+
:param func: The function to trace. When used as a decorator without parentheses,
|
|
587
|
+
this is the function being decorated. When used with parameters (e.g.,
|
|
588
|
+
``@trace(op="custom")``, this should be None.
|
|
589
|
+
:type func: Callable or None
|
|
590
|
+
|
|
591
|
+
:param op: The operation name for the span. This is a high-level description
|
|
592
|
+
of what the span represents (e.g., "http.client", "db.query").
|
|
593
|
+
You can use predefined constants from :py:class:`sentry_sdk.consts.OP`
|
|
594
|
+
or provide your own string. If not provided, a default operation will
|
|
595
|
+
be assigned based on the template.
|
|
596
|
+
:type op: str or None
|
|
597
|
+
|
|
598
|
+
:param name: The human-readable name/description for the span. If not provided,
|
|
599
|
+
defaults to the function name. This provides more specific details about
|
|
600
|
+
what the span represents (e.g., "GET /api/users", "process_user_data").
|
|
601
|
+
:type name: str or None
|
|
602
|
+
|
|
603
|
+
:param attributes: A dictionary of key-value pairs to add as attributes to the span.
|
|
604
|
+
Attribute values must be strings, integers, floats, or booleans. These
|
|
605
|
+
attributes provide additional context about the span's execution.
|
|
606
|
+
:type attributes: dict[str, Any] or None
|
|
607
|
+
|
|
608
|
+
:param template: The type of span to create. This determines what kind of
|
|
609
|
+
span instrumentation and data collection will be applied. Use predefined
|
|
610
|
+
constants from :py:class:`sentry_sdk.consts.SPANTEMPLATE`.
|
|
611
|
+
The default is `SPANTEMPLATE.DEFAULT` which is the right choice for most
|
|
612
|
+
use cases.
|
|
613
|
+
:type template: :py:class:`sentry_sdk.consts.SPANTEMPLATE`
|
|
614
|
+
|
|
615
|
+
:returns: When used as ``@trace``, returns the decorated function. When used as
|
|
616
|
+
``@trace(...)`` with parameters, returns a decorator function.
|
|
617
|
+
:rtype: Callable or decorator function
|
|
618
|
+
|
|
619
|
+
Example::
|
|
563
620
|
|
|
564
621
|
import sentry_sdk
|
|
622
|
+
from sentry_sdk.consts import OP, SPANTEMPLATE
|
|
565
623
|
|
|
624
|
+
# Simple usage with default values
|
|
566
625
|
@sentry_sdk.trace
|
|
567
|
-
def
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
626
|
+
def process_data():
|
|
627
|
+
# Function implementation
|
|
628
|
+
pass
|
|
629
|
+
|
|
630
|
+
# With custom parameters
|
|
631
|
+
@sentry_sdk.trace(
|
|
632
|
+
op=OP.DB_QUERY,
|
|
633
|
+
name="Get user data",
|
|
634
|
+
attributes={"postgres": True}
|
|
635
|
+
)
|
|
636
|
+
def make_db_query(sql):
|
|
637
|
+
# Function implementation
|
|
638
|
+
pass
|
|
639
|
+
|
|
640
|
+
# With a custom template
|
|
641
|
+
@sentry_sdk.trace(template=SPANTEMPLATE.AI_TOOL)
|
|
642
|
+
def calculate_interest_rate(amount, rate, years):
|
|
643
|
+
# Function implementation
|
|
644
|
+
pass
|
|
573
645
|
"""
|
|
574
|
-
from sentry_sdk.tracing_utils import
|
|
646
|
+
from sentry_sdk.tracing_utils import create_span_decorator
|
|
647
|
+
|
|
648
|
+
decorator = create_span_decorator(
|
|
649
|
+
op=op,
|
|
650
|
+
name=name,
|
|
651
|
+
attributes=attributes,
|
|
652
|
+
template=template,
|
|
653
|
+
)
|
|
575
654
|
|
|
576
|
-
# This patterns allows usage of both @sentry_traced and @sentry_traced(...)
|
|
577
|
-
# See https://stackoverflow.com/questions/52126071/decorator-with-arguments-avoid-parenthesis-when-no-arguments/52126278
|
|
578
655
|
if func:
|
|
579
|
-
return
|
|
656
|
+
return decorator(func)
|
|
580
657
|
else:
|
|
581
|
-
return
|
|
658
|
+
return decorator
|