sentry-sdk 2.40.0__py2.py3-none-any.whl → 2.42.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.
Potentially problematic release.
This version of sentry-sdk might be problematic. Click here for more details.
- sentry_sdk/_metrics.py +81 -0
- sentry_sdk/_metrics_batcher.py +156 -0
- sentry_sdk/_types.py +27 -22
- sentry_sdk/ai/__init__.py +7 -0
- sentry_sdk/ai/utils.py +48 -0
- sentry_sdk/client.py +81 -30
- sentry_sdk/consts.py +13 -8
- sentry_sdk/envelope.py +3 -3
- sentry_sdk/integrations/__init__.py +1 -0
- sentry_sdk/integrations/aiohttp.py +4 -1
- sentry_sdk/integrations/anthropic.py +10 -2
- sentry_sdk/integrations/google_genai/__init__.py +298 -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 +566 -0
- sentry_sdk/integrations/httpx.py +16 -5
- sentry_sdk/integrations/langchain.py +29 -4
- sentry_sdk/integrations/langgraph.py +5 -3
- sentry_sdk/integrations/logging.py +1 -1
- sentry_sdk/integrations/loguru.py +1 -1
- sentry_sdk/integrations/openai.py +3 -2
- sentry_sdk/integrations/openai_agents/spans/invoke_agent.py +10 -2
- sentry_sdk/integrations/openai_agents/utils.py +35 -18
- sentry_sdk/integrations/ray.py +20 -4
- sentry_sdk/integrations/stdlib.py +8 -1
- sentry_sdk/integrations/threading.py +52 -8
- sentry_sdk/logger.py +1 -1
- sentry_sdk/tracing.py +0 -26
- sentry_sdk/tracing_utils.py +64 -24
- sentry_sdk/transport.py +1 -17
- sentry_sdk/types.py +3 -0
- sentry_sdk/utils.py +17 -1
- {sentry_sdk-2.40.0.dist-info → sentry_sdk-2.42.0.dist-info}/METADATA +3 -1
- {sentry_sdk-2.40.0.dist-info → sentry_sdk-2.42.0.dist-info}/RECORD +38 -33
- sentry_sdk/metrics.py +0 -971
- {sentry_sdk-2.40.0.dist-info → sentry_sdk-2.42.0.dist-info}/WHEEL +0 -0
- {sentry_sdk-2.40.0.dist-info → sentry_sdk-2.42.0.dist-info}/entry_points.txt +0 -0
- {sentry_sdk-2.40.0.dist-info → sentry_sdk-2.42.0.dist-info}/licenses/LICENSE +0 -0
- {sentry_sdk-2.40.0.dist-info → sentry_sdk-2.42.0.dist-info}/top_level.txt +0 -0
|
@@ -2,7 +2,7 @@ from functools import wraps
|
|
|
2
2
|
from typing import Any, Callable, List, Optional
|
|
3
3
|
|
|
4
4
|
import sentry_sdk
|
|
5
|
-
from sentry_sdk.ai.utils import set_data_normalized
|
|
5
|
+
from sentry_sdk.ai.utils import set_data_normalized, normalize_message_roles
|
|
6
6
|
from sentry_sdk.consts import OP, SPANDATA
|
|
7
7
|
from sentry_sdk.integrations import DidNotEnable, Integration
|
|
8
8
|
from sentry_sdk.scope import should_send_default_pii
|
|
@@ -180,10 +180,11 @@ def _wrap_pregel_invoke(f):
|
|
|
180
180
|
):
|
|
181
181
|
input_messages = _parse_langgraph_messages(args[0])
|
|
182
182
|
if input_messages:
|
|
183
|
+
normalized_input_messages = normalize_message_roles(input_messages)
|
|
183
184
|
set_data_normalized(
|
|
184
185
|
span,
|
|
185
186
|
SPANDATA.GEN_AI_REQUEST_MESSAGES,
|
|
186
|
-
|
|
187
|
+
normalized_input_messages,
|
|
187
188
|
unpack=False,
|
|
188
189
|
)
|
|
189
190
|
|
|
@@ -230,10 +231,11 @@ def _wrap_pregel_ainvoke(f):
|
|
|
230
231
|
):
|
|
231
232
|
input_messages = _parse_langgraph_messages(args[0])
|
|
232
233
|
if input_messages:
|
|
234
|
+
normalized_input_messages = normalize_message_roles(input_messages)
|
|
233
235
|
set_data_normalized(
|
|
234
236
|
span,
|
|
235
237
|
SPANDATA.GEN_AI_REQUEST_MESSAGES,
|
|
236
|
-
|
|
238
|
+
normalized_input_messages,
|
|
237
239
|
unpack=False,
|
|
238
240
|
)
|
|
239
241
|
|
|
@@ -409,7 +409,7 @@ class SentryLogsHandler(_BaseHandler):
|
|
|
409
409
|
attrs["logger.name"] = record.name
|
|
410
410
|
|
|
411
411
|
# noinspection PyProtectedMember
|
|
412
|
-
client.
|
|
412
|
+
client._capture_log(
|
|
413
413
|
{
|
|
414
414
|
"severity_text": otel_severity_text,
|
|
415
415
|
"severity_number": otel_severity_number,
|
|
@@ -193,7 +193,7 @@ def loguru_sentry_logs_handler(message):
|
|
|
193
193
|
if record.get("name"):
|
|
194
194
|
attrs["logger.name"] = record["name"]
|
|
195
195
|
|
|
196
|
-
client.
|
|
196
|
+
client._capture_log(
|
|
197
197
|
{
|
|
198
198
|
"severity_text": otel_severity_text,
|
|
199
199
|
"severity_number": otel_severity_number,
|
|
@@ -3,7 +3,7 @@ from functools import wraps
|
|
|
3
3
|
import sentry_sdk
|
|
4
4
|
from sentry_sdk import consts
|
|
5
5
|
from sentry_sdk.ai.monitoring import record_token_usage
|
|
6
|
-
from sentry_sdk.ai.utils import set_data_normalized
|
|
6
|
+
from sentry_sdk.ai.utils import set_data_normalized, normalize_message_roles
|
|
7
7
|
from sentry_sdk.consts import SPANDATA
|
|
8
8
|
from sentry_sdk.integrations import DidNotEnable, Integration
|
|
9
9
|
from sentry_sdk.scope import should_send_default_pii
|
|
@@ -182,8 +182,9 @@ def _set_input_data(span, kwargs, operation, integration):
|
|
|
182
182
|
and should_send_default_pii()
|
|
183
183
|
and integration.include_prompts
|
|
184
184
|
):
|
|
185
|
+
normalized_messages = normalize_message_roles(messages)
|
|
185
186
|
set_data_normalized(
|
|
186
|
-
span, SPANDATA.GEN_AI_REQUEST_MESSAGES,
|
|
187
|
+
span, SPANDATA.GEN_AI_REQUEST_MESSAGES, normalized_messages, unpack=False
|
|
187
188
|
)
|
|
188
189
|
|
|
189
190
|
# Input attributes: Common
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import sentry_sdk
|
|
2
|
-
from sentry_sdk.ai.utils import
|
|
2
|
+
from sentry_sdk.ai.utils import (
|
|
3
|
+
get_start_span_function,
|
|
4
|
+
set_data_normalized,
|
|
5
|
+
normalize_message_roles,
|
|
6
|
+
)
|
|
3
7
|
from sentry_sdk.consts import OP, SPANDATA
|
|
4
8
|
from sentry_sdk.scope import should_send_default_pii
|
|
5
9
|
from sentry_sdk.utils import safe_serialize
|
|
@@ -56,8 +60,12 @@ def invoke_agent_span(context, agent, kwargs):
|
|
|
56
60
|
)
|
|
57
61
|
|
|
58
62
|
if len(messages) > 0:
|
|
63
|
+
normalized_messages = normalize_message_roles(messages)
|
|
59
64
|
set_data_normalized(
|
|
60
|
-
span,
|
|
65
|
+
span,
|
|
66
|
+
SPANDATA.GEN_AI_REQUEST_MESSAGES,
|
|
67
|
+
normalized_messages,
|
|
68
|
+
unpack=False,
|
|
61
69
|
)
|
|
62
70
|
|
|
63
71
|
_set_agent_data(span, agent)
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import sentry_sdk
|
|
2
|
-
from sentry_sdk.ai.utils import
|
|
2
|
+
from sentry_sdk.ai.utils import (
|
|
3
|
+
GEN_AI_ALLOWED_MESSAGE_ROLES,
|
|
4
|
+
normalize_message_roles,
|
|
5
|
+
set_data_normalized,
|
|
6
|
+
normalize_message_role,
|
|
7
|
+
)
|
|
3
8
|
from sentry_sdk.consts import SPANDATA, SPANSTATUS, OP
|
|
4
9
|
from sentry_sdk.integrations import DidNotEnable
|
|
5
10
|
from sentry_sdk.scope import should_send_default_pii
|
|
@@ -94,35 +99,47 @@ def _set_input_data(span, get_response_kwargs):
|
|
|
94
99
|
# type: (sentry_sdk.tracing.Span, dict[str, Any]) -> None
|
|
95
100
|
if not should_send_default_pii():
|
|
96
101
|
return
|
|
102
|
+
request_messages = []
|
|
97
103
|
|
|
98
|
-
messages_by_role = {
|
|
99
|
-
"system": [],
|
|
100
|
-
"user": [],
|
|
101
|
-
"assistant": [],
|
|
102
|
-
"tool": [],
|
|
103
|
-
} # type: (dict[str, list[Any]])
|
|
104
104
|
system_instructions = get_response_kwargs.get("system_instructions")
|
|
105
105
|
if system_instructions:
|
|
106
|
-
|
|
106
|
+
request_messages.append(
|
|
107
|
+
{
|
|
108
|
+
"role": GEN_AI_ALLOWED_MESSAGE_ROLES.SYSTEM,
|
|
109
|
+
"content": [{"type": "text", "text": system_instructions}],
|
|
110
|
+
}
|
|
111
|
+
)
|
|
107
112
|
|
|
108
113
|
for message in get_response_kwargs.get("input", []):
|
|
109
114
|
if "role" in message:
|
|
110
|
-
|
|
111
|
-
|
|
115
|
+
normalized_role = normalize_message_role(message.get("role"))
|
|
116
|
+
request_messages.append(
|
|
117
|
+
{
|
|
118
|
+
"role": normalized_role,
|
|
119
|
+
"content": [{"type": "text", "text": message.get("content")}],
|
|
120
|
+
}
|
|
112
121
|
)
|
|
113
122
|
else:
|
|
114
123
|
if message.get("type") == "function_call":
|
|
115
|
-
|
|
124
|
+
request_messages.append(
|
|
125
|
+
{
|
|
126
|
+
"role": GEN_AI_ALLOWED_MESSAGE_ROLES.ASSISTANT,
|
|
127
|
+
"content": [message],
|
|
128
|
+
}
|
|
129
|
+
)
|
|
116
130
|
elif message.get("type") == "function_call_output":
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
131
|
+
request_messages.append(
|
|
132
|
+
{
|
|
133
|
+
"role": GEN_AI_ALLOWED_MESSAGE_ROLES.TOOL,
|
|
134
|
+
"content": [message],
|
|
135
|
+
}
|
|
136
|
+
)
|
|
123
137
|
|
|
124
138
|
set_data_normalized(
|
|
125
|
-
span,
|
|
139
|
+
span,
|
|
140
|
+
SPANDATA.GEN_AI_REQUEST_MESSAGES,
|
|
141
|
+
normalize_message_roles(request_messages),
|
|
142
|
+
unpack=False,
|
|
126
143
|
)
|
|
127
144
|
|
|
128
145
|
|
sentry_sdk/integrations/ray.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import inspect
|
|
2
|
+
import functools
|
|
2
3
|
import sys
|
|
3
4
|
|
|
4
5
|
import sentry_sdk
|
|
@@ -17,7 +18,6 @@ try:
|
|
|
17
18
|
import ray # type: ignore[import-not-found]
|
|
18
19
|
except ImportError:
|
|
19
20
|
raise DidNotEnable("Ray not installed.")
|
|
20
|
-
import functools
|
|
21
21
|
|
|
22
22
|
from typing import TYPE_CHECKING
|
|
23
23
|
|
|
@@ -54,12 +54,13 @@ def _patch_ray_remote():
|
|
|
54
54
|
|
|
55
55
|
def wrapper(user_f):
|
|
56
56
|
# type: (Callable[..., Any]) -> Any
|
|
57
|
-
|
|
57
|
+
@functools.wraps(user_f)
|
|
58
|
+
def new_func(*f_args, _sentry_tracing=None, **f_kwargs):
|
|
58
59
|
# type: (Any, Optional[dict[str, Any]], Any) -> Any
|
|
59
60
|
_check_sentry_initialized()
|
|
60
61
|
|
|
61
62
|
transaction = sentry_sdk.continue_trace(
|
|
62
|
-
|
|
63
|
+
_sentry_tracing or {},
|
|
63
64
|
op=OP.QUEUE_TASK_RAY,
|
|
64
65
|
name=qualname_from_function(user_f),
|
|
65
66
|
origin=RayIntegration.origin,
|
|
@@ -78,6 +79,19 @@ def _patch_ray_remote():
|
|
|
78
79
|
|
|
79
80
|
return result
|
|
80
81
|
|
|
82
|
+
# Patching new_func signature to add the _sentry_tracing parameter to it
|
|
83
|
+
# Ray later inspects the signature and finds the unexpected parameter otherwise
|
|
84
|
+
signature = inspect.signature(new_func)
|
|
85
|
+
params = list(signature.parameters.values())
|
|
86
|
+
params.append(
|
|
87
|
+
inspect.Parameter(
|
|
88
|
+
"_sentry_tracing",
|
|
89
|
+
kind=inspect.Parameter.KEYWORD_ONLY,
|
|
90
|
+
default=None,
|
|
91
|
+
)
|
|
92
|
+
)
|
|
93
|
+
new_func.__signature__ = signature.replace(parameters=params) # type: ignore[attr-defined]
|
|
94
|
+
|
|
81
95
|
if f:
|
|
82
96
|
rv = old_remote(new_func)
|
|
83
97
|
else:
|
|
@@ -99,7 +113,9 @@ def _patch_ray_remote():
|
|
|
99
113
|
for k, v in sentry_sdk.get_current_scope().iter_trace_propagation_headers()
|
|
100
114
|
}
|
|
101
115
|
try:
|
|
102
|
-
result = old_remote_method(
|
|
116
|
+
result = old_remote_method(
|
|
117
|
+
*args, **kwargs, _sentry_tracing=tracing
|
|
118
|
+
)
|
|
103
119
|
span.set_status(SPANSTATUS.OK)
|
|
104
120
|
except Exception:
|
|
105
121
|
span.set_status(SPANSTATUS.INTERNAL_ERROR)
|
|
@@ -8,7 +8,11 @@ import sentry_sdk
|
|
|
8
8
|
from sentry_sdk.consts import OP, SPANDATA
|
|
9
9
|
from sentry_sdk.integrations import Integration
|
|
10
10
|
from sentry_sdk.scope import add_global_event_processor
|
|
11
|
-
from sentry_sdk.tracing_utils import
|
|
11
|
+
from sentry_sdk.tracing_utils import (
|
|
12
|
+
EnvironHeaders,
|
|
13
|
+
should_propagate_trace,
|
|
14
|
+
add_http_request_source,
|
|
15
|
+
)
|
|
12
16
|
from sentry_sdk.utils import (
|
|
13
17
|
SENSITIVE_DATA_SUBSTITUTE,
|
|
14
18
|
capture_internal_exceptions,
|
|
@@ -135,6 +139,9 @@ def _install_httplib():
|
|
|
135
139
|
finally:
|
|
136
140
|
span.finish()
|
|
137
141
|
|
|
142
|
+
with capture_internal_exceptions():
|
|
143
|
+
add_http_request_source(span)
|
|
144
|
+
|
|
138
145
|
return rv
|
|
139
146
|
|
|
140
147
|
HTTPConnection.putrequest = putrequest # type: ignore[method-assign]
|
|
@@ -2,6 +2,7 @@ import sys
|
|
|
2
2
|
import warnings
|
|
3
3
|
from functools import wraps
|
|
4
4
|
from threading import Thread, current_thread
|
|
5
|
+
from concurrent.futures import ThreadPoolExecutor, Future
|
|
5
6
|
|
|
6
7
|
import sentry_sdk
|
|
7
8
|
from sentry_sdk.integrations import Integration
|
|
@@ -24,6 +25,7 @@ if TYPE_CHECKING:
|
|
|
24
25
|
from sentry_sdk._types import ExcInfo
|
|
25
26
|
|
|
26
27
|
F = TypeVar("F", bound=Callable[..., Any])
|
|
28
|
+
T = TypeVar("T", bound=Any)
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
class ThreadingIntegration(Integration):
|
|
@@ -59,6 +61,15 @@ class ThreadingIntegration(Integration):
|
|
|
59
61
|
django_version = None
|
|
60
62
|
channels_version = None
|
|
61
63
|
|
|
64
|
+
is_async_emulated_with_threads = (
|
|
65
|
+
sys.version_info < (3, 9)
|
|
66
|
+
and channels_version is not None
|
|
67
|
+
and channels_version < "4.0.0"
|
|
68
|
+
and django_version is not None
|
|
69
|
+
and django_version >= (3, 0)
|
|
70
|
+
and django_version < (4, 0)
|
|
71
|
+
)
|
|
72
|
+
|
|
62
73
|
@wraps(old_start)
|
|
63
74
|
def sentry_start(self, *a, **kw):
|
|
64
75
|
# type: (Thread, *Any, **Any) -> Any
|
|
@@ -67,14 +78,7 @@ class ThreadingIntegration(Integration):
|
|
|
67
78
|
return old_start(self, *a, **kw)
|
|
68
79
|
|
|
69
80
|
if integration.propagate_scope:
|
|
70
|
-
if
|
|
71
|
-
sys.version_info < (3, 9)
|
|
72
|
-
and channels_version is not None
|
|
73
|
-
and channels_version < "4.0.0"
|
|
74
|
-
and django_version is not None
|
|
75
|
-
and django_version >= (3, 0)
|
|
76
|
-
and django_version < (4, 0)
|
|
77
|
-
):
|
|
81
|
+
if is_async_emulated_with_threads:
|
|
78
82
|
warnings.warn(
|
|
79
83
|
"There is a known issue with Django channels 2.x and 3.x when using Python 3.8 or older. "
|
|
80
84
|
"(Async support is emulated using threads and some Sentry data may be leaked between those threads.) "
|
|
@@ -109,6 +113,9 @@ class ThreadingIntegration(Integration):
|
|
|
109
113
|
return old_start(self, *a, **kw)
|
|
110
114
|
|
|
111
115
|
Thread.start = sentry_start # type: ignore
|
|
116
|
+
ThreadPoolExecutor.submit = _wrap_threadpool_executor_submit( # type: ignore
|
|
117
|
+
ThreadPoolExecutor.submit, is_async_emulated_with_threads
|
|
118
|
+
)
|
|
112
119
|
|
|
113
120
|
|
|
114
121
|
def _wrap_run(isolation_scope_to_use, current_scope_to_use, old_run_func):
|
|
@@ -134,6 +141,43 @@ def _wrap_run(isolation_scope_to_use, current_scope_to_use, old_run_func):
|
|
|
134
141
|
return run # type: ignore
|
|
135
142
|
|
|
136
143
|
|
|
144
|
+
def _wrap_threadpool_executor_submit(func, is_async_emulated_with_threads):
|
|
145
|
+
# type: (Callable[..., Future[T]], bool) -> Callable[..., Future[T]]
|
|
146
|
+
"""
|
|
147
|
+
Wrap submit call to propagate scopes on task submission.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
@wraps(func)
|
|
151
|
+
def sentry_submit(self, fn, *args, **kwargs):
|
|
152
|
+
# type: (ThreadPoolExecutor, Callable[..., T], *Any, **Any) -> Future[T]
|
|
153
|
+
integration = sentry_sdk.get_client().get_integration(ThreadingIntegration)
|
|
154
|
+
if integration is None:
|
|
155
|
+
return func(self, fn, *args, **kwargs)
|
|
156
|
+
|
|
157
|
+
if integration.propagate_scope and is_async_emulated_with_threads:
|
|
158
|
+
isolation_scope = sentry_sdk.get_isolation_scope()
|
|
159
|
+
current_scope = sentry_sdk.get_current_scope()
|
|
160
|
+
elif integration.propagate_scope:
|
|
161
|
+
isolation_scope = sentry_sdk.get_isolation_scope().fork()
|
|
162
|
+
current_scope = sentry_sdk.get_current_scope().fork()
|
|
163
|
+
else:
|
|
164
|
+
isolation_scope = None
|
|
165
|
+
current_scope = None
|
|
166
|
+
|
|
167
|
+
def wrapped_fn(*args, **kwargs):
|
|
168
|
+
# type: (*Any, **Any) -> Any
|
|
169
|
+
if isolation_scope is not None and current_scope is not None:
|
|
170
|
+
with use_isolation_scope(isolation_scope):
|
|
171
|
+
with use_scope(current_scope):
|
|
172
|
+
return fn(*args, **kwargs)
|
|
173
|
+
|
|
174
|
+
return fn(*args, **kwargs)
|
|
175
|
+
|
|
176
|
+
return func(self, wrapped_fn, *args, **kwargs)
|
|
177
|
+
|
|
178
|
+
return sentry_submit
|
|
179
|
+
|
|
180
|
+
|
|
137
181
|
def _capture_exception():
|
|
138
182
|
# type: () -> ExcInfo
|
|
139
183
|
exc_info = sys.exc_info()
|
sentry_sdk/logger.py
CHANGED
sentry_sdk/tracing.py
CHANGED
|
@@ -276,7 +276,6 @@ class Span:
|
|
|
276
276
|
"hub",
|
|
277
277
|
"_context_manager_state",
|
|
278
278
|
"_containing_transaction",
|
|
279
|
-
"_local_aggregator",
|
|
280
279
|
"scope",
|
|
281
280
|
"origin",
|
|
282
281
|
"name",
|
|
@@ -345,7 +344,6 @@ class Span:
|
|
|
345
344
|
self.timestamp = None # type: Optional[datetime]
|
|
346
345
|
|
|
347
346
|
self._span_recorder = None # type: Optional[_SpanRecorder]
|
|
348
|
-
self._local_aggregator = None # type: Optional[LocalAggregator]
|
|
349
347
|
|
|
350
348
|
self.update_active_thread()
|
|
351
349
|
self.set_profiler_id(get_profiler_id())
|
|
@@ -383,13 +381,6 @@ class Span:
|
|
|
383
381
|
# type: (str) -> None
|
|
384
382
|
self._span_id = value
|
|
385
383
|
|
|
386
|
-
def _get_local_aggregator(self):
|
|
387
|
-
# type: (...) -> LocalAggregator
|
|
388
|
-
rv = self._local_aggregator
|
|
389
|
-
if rv is None:
|
|
390
|
-
rv = self._local_aggregator = LocalAggregator()
|
|
391
|
-
return rv
|
|
392
|
-
|
|
393
384
|
def __repr__(self):
|
|
394
385
|
# type: () -> str
|
|
395
386
|
return (
|
|
@@ -741,11 +732,6 @@ class Span:
|
|
|
741
732
|
if self.status:
|
|
742
733
|
self._tags["status"] = self.status
|
|
743
734
|
|
|
744
|
-
if self._local_aggregator is not None:
|
|
745
|
-
metrics_summary = self._local_aggregator.to_json()
|
|
746
|
-
if metrics_summary:
|
|
747
|
-
rv["_metrics_summary"] = metrics_summary
|
|
748
|
-
|
|
749
735
|
if len(self._measurements) > 0:
|
|
750
736
|
rv["measurements"] = self._measurements
|
|
751
737
|
|
|
@@ -1122,13 +1108,6 @@ class Transaction(Span):
|
|
|
1122
1108
|
|
|
1123
1109
|
event["measurements"] = self._measurements
|
|
1124
1110
|
|
|
1125
|
-
# This is here since `to_json` is not invoked. This really should
|
|
1126
|
-
# be gone when we switch to onlyspans.
|
|
1127
|
-
if self._local_aggregator is not None:
|
|
1128
|
-
metrics_summary = self._local_aggregator.to_json()
|
|
1129
|
-
if metrics_summary:
|
|
1130
|
-
event["_metrics_summary"] = metrics_summary
|
|
1131
|
-
|
|
1132
1111
|
return scope.capture_event(event)
|
|
1133
1112
|
|
|
1134
1113
|
def set_measurement(self, name, value, unit=""):
|
|
@@ -1505,8 +1484,3 @@ from sentry_sdk.tracing_utils import (
|
|
|
1505
1484
|
has_tracing_enabled,
|
|
1506
1485
|
maybe_create_breadcrumbs_from_span,
|
|
1507
1486
|
)
|
|
1508
|
-
|
|
1509
|
-
with warnings.catch_warnings():
|
|
1510
|
-
# The code in this file which uses `LocalAggregator` is only called from the deprecated `metrics` module.
|
|
1511
|
-
warnings.simplefilter("ignore", DeprecationWarning)
|
|
1512
|
-
from sentry_sdk.metrics import LocalAggregator
|
sentry_sdk/tracing_utils.py
CHANGED
|
@@ -218,33 +218,11 @@ def _should_be_included(
|
|
|
218
218
|
)
|
|
219
219
|
|
|
220
220
|
|
|
221
|
-
def
|
|
222
|
-
# type: (sentry_sdk.tracing.Span) -> None
|
|
221
|
+
def add_source(span, project_root, in_app_include, in_app_exclude):
|
|
222
|
+
# type: (sentry_sdk.tracing.Span, Optional[str], Optional[list[str]], Optional[list[str]]) -> None
|
|
223
223
|
"""
|
|
224
224
|
Adds OTel compatible source code information to the span
|
|
225
225
|
"""
|
|
226
|
-
client = sentry_sdk.get_client()
|
|
227
|
-
if not client.is_active():
|
|
228
|
-
return
|
|
229
|
-
|
|
230
|
-
if span.timestamp is None or span.start_timestamp is None:
|
|
231
|
-
return
|
|
232
|
-
|
|
233
|
-
should_add_query_source = client.options.get("enable_db_query_source", True)
|
|
234
|
-
if not should_add_query_source:
|
|
235
|
-
return
|
|
236
|
-
|
|
237
|
-
duration = span.timestamp - span.start_timestamp
|
|
238
|
-
threshold = client.options.get("db_query_source_threshold_ms", 0)
|
|
239
|
-
slow_query = duration / timedelta(milliseconds=1) > threshold
|
|
240
|
-
|
|
241
|
-
if not slow_query:
|
|
242
|
-
return
|
|
243
|
-
|
|
244
|
-
project_root = client.options["project_root"]
|
|
245
|
-
in_app_include = client.options.get("in_app_include")
|
|
246
|
-
in_app_exclude = client.options.get("in_app_exclude")
|
|
247
|
-
|
|
248
226
|
# Find the correct frame
|
|
249
227
|
frame = sys._getframe() # type: Union[FrameType, None]
|
|
250
228
|
while frame is not None:
|
|
@@ -309,6 +287,68 @@ def add_query_source(span):
|
|
|
309
287
|
span.set_data(SPANDATA.CODE_FUNCTION, frame.f_code.co_name)
|
|
310
288
|
|
|
311
289
|
|
|
290
|
+
def add_query_source(span):
|
|
291
|
+
# type: (sentry_sdk.tracing.Span) -> None
|
|
292
|
+
"""
|
|
293
|
+
Adds OTel compatible source code information to a database query span
|
|
294
|
+
"""
|
|
295
|
+
client = sentry_sdk.get_client()
|
|
296
|
+
if not client.is_active():
|
|
297
|
+
return
|
|
298
|
+
|
|
299
|
+
if span.timestamp is None or span.start_timestamp is None:
|
|
300
|
+
return
|
|
301
|
+
|
|
302
|
+
should_add_query_source = client.options.get("enable_db_query_source", True)
|
|
303
|
+
if not should_add_query_source:
|
|
304
|
+
return
|
|
305
|
+
|
|
306
|
+
duration = span.timestamp - span.start_timestamp
|
|
307
|
+
threshold = client.options.get("db_query_source_threshold_ms", 0)
|
|
308
|
+
slow_query = duration / timedelta(milliseconds=1) > threshold
|
|
309
|
+
|
|
310
|
+
if not slow_query:
|
|
311
|
+
return
|
|
312
|
+
|
|
313
|
+
add_source(
|
|
314
|
+
span=span,
|
|
315
|
+
project_root=client.options["project_root"],
|
|
316
|
+
in_app_include=client.options.get("in_app_include"),
|
|
317
|
+
in_app_exclude=client.options.get("in_app_exclude"),
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def add_http_request_source(span):
|
|
322
|
+
# type: (sentry_sdk.tracing.Span) -> None
|
|
323
|
+
"""
|
|
324
|
+
Adds OTel compatible source code information to a span for an outgoing HTTP request
|
|
325
|
+
"""
|
|
326
|
+
client = sentry_sdk.get_client()
|
|
327
|
+
if not client.is_active():
|
|
328
|
+
return
|
|
329
|
+
|
|
330
|
+
if span.timestamp is None or span.start_timestamp is None:
|
|
331
|
+
return
|
|
332
|
+
|
|
333
|
+
should_add_request_source = client.options.get("enable_http_request_source", False)
|
|
334
|
+
if not should_add_request_source:
|
|
335
|
+
return
|
|
336
|
+
|
|
337
|
+
duration = span.timestamp - span.start_timestamp
|
|
338
|
+
threshold = client.options.get("http_request_source_threshold_ms", 0)
|
|
339
|
+
slow_query = duration / timedelta(milliseconds=1) > threshold
|
|
340
|
+
|
|
341
|
+
if not slow_query:
|
|
342
|
+
return
|
|
343
|
+
|
|
344
|
+
add_source(
|
|
345
|
+
span=span,
|
|
346
|
+
project_root=client.options["project_root"],
|
|
347
|
+
in_app_include=client.options.get("in_app_include"),
|
|
348
|
+
in_app_exclude=client.options.get("in_app_exclude"),
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
|
|
312
352
|
def extract_sentrytrace_data(header):
|
|
313
353
|
# type: (Optional[str]) -> Optional[Dict[str, Union[str, bool, None]]]
|
|
314
354
|
"""
|
sentry_sdk/transport.py
CHANGED
|
@@ -171,17 +171,7 @@ def _parse_rate_limits(header, now=None):
|
|
|
171
171
|
|
|
172
172
|
retry_after = now + timedelta(seconds=int(retry_after_val))
|
|
173
173
|
for category in categories and categories.split(";") or (None,):
|
|
174
|
-
|
|
175
|
-
try:
|
|
176
|
-
namespaces = parameters[4].split(";")
|
|
177
|
-
except IndexError:
|
|
178
|
-
namespaces = []
|
|
179
|
-
|
|
180
|
-
if not namespaces or "custom" in namespaces:
|
|
181
|
-
yield category, retry_after # type: ignore
|
|
182
|
-
|
|
183
|
-
else:
|
|
184
|
-
yield category, retry_after # type: ignore
|
|
174
|
+
yield category, retry_after # type: ignore
|
|
185
175
|
except (LookupError, ValueError):
|
|
186
176
|
continue
|
|
187
177
|
|
|
@@ -417,12 +407,6 @@ class BaseHttpTransport(Transport):
|
|
|
417
407
|
# type: (str) -> bool
|
|
418
408
|
def _disabled(bucket):
|
|
419
409
|
# type: (Any) -> bool
|
|
420
|
-
|
|
421
|
-
# The envelope item type used for metrics is statsd
|
|
422
|
-
# whereas the rate limit category is metric_bucket
|
|
423
|
-
if bucket == "statsd":
|
|
424
|
-
bucket = "metric_bucket"
|
|
425
|
-
|
|
426
410
|
ts = self._disabled_until.get(bucket)
|
|
427
411
|
return ts is not None and ts > datetime.now(timezone.utc)
|
|
428
412
|
|
sentry_sdk/types.py
CHANGED
|
@@ -21,6 +21,7 @@ if TYPE_CHECKING:
|
|
|
21
21
|
Log,
|
|
22
22
|
MonitorConfig,
|
|
23
23
|
SamplingContext,
|
|
24
|
+
Metric,
|
|
24
25
|
)
|
|
25
26
|
else:
|
|
26
27
|
from typing import Any
|
|
@@ -35,6 +36,7 @@ else:
|
|
|
35
36
|
Log = Any
|
|
36
37
|
MonitorConfig = Any
|
|
37
38
|
SamplingContext = Any
|
|
39
|
+
Metric = Any
|
|
38
40
|
|
|
39
41
|
|
|
40
42
|
__all__ = (
|
|
@@ -46,4 +48,5 @@ __all__ = (
|
|
|
46
48
|
"Log",
|
|
47
49
|
"MonitorConfig",
|
|
48
50
|
"SamplingContext",
|
|
51
|
+
"Metric",
|
|
49
52
|
)
|
sentry_sdk/utils.py
CHANGED
|
@@ -59,7 +59,7 @@ if TYPE_CHECKING:
|
|
|
59
59
|
|
|
60
60
|
from gevent.hub import Hub
|
|
61
61
|
|
|
62
|
-
from sentry_sdk._types import Event, ExcInfo, Log, Hint
|
|
62
|
+
from sentry_sdk._types import Event, ExcInfo, Log, Hint, Metric
|
|
63
63
|
|
|
64
64
|
P = ParamSpec("P")
|
|
65
65
|
R = TypeVar("R")
|
|
@@ -2013,3 +2013,19 @@ def get_before_send_log(options):
|
|
|
2013
2013
|
return options.get("before_send_log") or options["_experiments"].get(
|
|
2014
2014
|
"before_send_log"
|
|
2015
2015
|
)
|
|
2016
|
+
|
|
2017
|
+
|
|
2018
|
+
def has_metrics_enabled(options):
|
|
2019
|
+
# type: (Optional[dict[str, Any]]) -> bool
|
|
2020
|
+
if options is None:
|
|
2021
|
+
return False
|
|
2022
|
+
|
|
2023
|
+
return bool(options["_experiments"].get("enable_metrics", False))
|
|
2024
|
+
|
|
2025
|
+
|
|
2026
|
+
def get_before_send_metric(options):
|
|
2027
|
+
# type: (Optional[dict[str, Any]]) -> Optional[Callable[[Metric, Hint], Optional[Metric]]]
|
|
2028
|
+
if options is None:
|
|
2029
|
+
return None
|
|
2030
|
+
|
|
2031
|
+
return options["_experiments"].get("before_send_metric")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sentry-sdk
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.42.0
|
|
4
4
|
Summary: Python client for Sentry (https://sentry.io)
|
|
5
5
|
Home-page: https://github.com/getsentry/sentry-python
|
|
6
6
|
Author: Sentry Team and Contributors
|
|
@@ -118,6 +118,8 @@ Provides-Extra: tornado
|
|
|
118
118
|
Requires-Dist: tornado>=6; extra == "tornado"
|
|
119
119
|
Provides-Extra: unleash
|
|
120
120
|
Requires-Dist: UnleashClient>=6.0.1; extra == "unleash"
|
|
121
|
+
Provides-Extra: google-genai
|
|
122
|
+
Requires-Dist: google-genai>=1.29.0; extra == "google-genai"
|
|
121
123
|
Dynamic: author
|
|
122
124
|
Dynamic: author-email
|
|
123
125
|
Dynamic: classifier
|