sentry-sdk 2.30.0__py2.py3-none-any.whl → 3.0.0a2__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sentry-sdk might be problematic. Click here for more details.
- sentry_sdk/__init__.py +3 -8
- sentry_sdk/_compat.py +0 -1
- sentry_sdk/_init_implementation.py +6 -44
- sentry_sdk/_types.py +2 -64
- sentry_sdk/ai/monitoring.py +14 -10
- sentry_sdk/ai/utils.py +1 -1
- sentry_sdk/api.py +56 -169
- sentry_sdk/client.py +27 -72
- sentry_sdk/consts.py +60 -23
- sentry_sdk/debug.py +0 -10
- sentry_sdk/envelope.py +1 -3
- sentry_sdk/feature_flags.py +1 -1
- sentry_sdk/integrations/__init__.py +4 -2
- sentry_sdk/integrations/_asgi_common.py +5 -6
- sentry_sdk/integrations/_wsgi_common.py +11 -40
- sentry_sdk/integrations/aiohttp.py +104 -57
- sentry_sdk/integrations/anthropic.py +10 -7
- sentry_sdk/integrations/arq.py +24 -13
- sentry_sdk/integrations/asgi.py +102 -83
- sentry_sdk/integrations/asyncio.py +1 -0
- sentry_sdk/integrations/asyncpg.py +45 -30
- sentry_sdk/integrations/aws_lambda.py +109 -92
- sentry_sdk/integrations/boto3.py +38 -9
- sentry_sdk/integrations/bottle.py +1 -1
- sentry_sdk/integrations/celery/__init__.py +51 -41
- sentry_sdk/integrations/clickhouse_driver.py +59 -28
- sentry_sdk/integrations/cohere.py +2 -0
- sentry_sdk/integrations/django/__init__.py +25 -46
- sentry_sdk/integrations/django/asgi.py +6 -2
- sentry_sdk/integrations/django/caching.py +13 -22
- sentry_sdk/integrations/django/middleware.py +1 -0
- sentry_sdk/integrations/django/signals_handlers.py +3 -1
- sentry_sdk/integrations/django/templates.py +8 -12
- sentry_sdk/integrations/django/transactions.py +1 -6
- sentry_sdk/integrations/django/views.py +5 -2
- sentry_sdk/integrations/falcon.py +7 -25
- sentry_sdk/integrations/fastapi.py +3 -3
- sentry_sdk/integrations/flask.py +1 -1
- sentry_sdk/integrations/gcp.py +63 -38
- sentry_sdk/integrations/graphene.py +6 -13
- sentry_sdk/integrations/grpc/aio/client.py +14 -8
- sentry_sdk/integrations/grpc/aio/server.py +19 -21
- sentry_sdk/integrations/grpc/client.py +8 -6
- sentry_sdk/integrations/grpc/server.py +12 -14
- sentry_sdk/integrations/httpx.py +47 -12
- sentry_sdk/integrations/huey.py +26 -22
- sentry_sdk/integrations/huggingface_hub.py +1 -0
- sentry_sdk/integrations/langchain.py +22 -15
- sentry_sdk/integrations/litestar.py +4 -2
- sentry_sdk/integrations/logging.py +7 -2
- sentry_sdk/integrations/openai.py +2 -0
- sentry_sdk/integrations/pymongo.py +18 -25
- sentry_sdk/integrations/pyramid.py +1 -1
- sentry_sdk/integrations/quart.py +3 -3
- sentry_sdk/integrations/ray.py +23 -17
- sentry_sdk/integrations/redis/_async_common.py +29 -18
- sentry_sdk/integrations/redis/_sync_common.py +28 -19
- sentry_sdk/integrations/redis/modules/caches.py +13 -10
- sentry_sdk/integrations/redis/modules/queries.py +14 -11
- sentry_sdk/integrations/redis/rb.py +4 -4
- sentry_sdk/integrations/redis/redis.py +6 -6
- sentry_sdk/integrations/redis/redis_cluster.py +18 -18
- sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +4 -4
- sentry_sdk/integrations/redis/utils.py +64 -24
- sentry_sdk/integrations/rq.py +68 -23
- sentry_sdk/integrations/rust_tracing.py +28 -43
- sentry_sdk/integrations/sanic.py +23 -13
- sentry_sdk/integrations/socket.py +9 -5
- sentry_sdk/integrations/sqlalchemy.py +8 -8
- sentry_sdk/integrations/starlette.py +11 -31
- sentry_sdk/integrations/starlite.py +4 -2
- sentry_sdk/integrations/stdlib.py +56 -9
- sentry_sdk/integrations/strawberry.py +40 -59
- sentry_sdk/integrations/threading.py +10 -26
- sentry_sdk/integrations/tornado.py +57 -18
- sentry_sdk/integrations/trytond.py +4 -1
- sentry_sdk/integrations/wsgi.py +84 -38
- sentry_sdk/opentelemetry/__init__.py +9 -0
- sentry_sdk/opentelemetry/consts.py +33 -0
- sentry_sdk/opentelemetry/contextvars_context.py +81 -0
- sentry_sdk/{integrations/opentelemetry → opentelemetry}/propagator.py +19 -28
- sentry_sdk/opentelemetry/sampler.py +326 -0
- sentry_sdk/opentelemetry/scope.py +218 -0
- sentry_sdk/opentelemetry/span_processor.py +335 -0
- sentry_sdk/opentelemetry/tracing.py +59 -0
- sentry_sdk/opentelemetry/utils.py +484 -0
- sentry_sdk/profiler/__init__.py +0 -40
- sentry_sdk/profiler/continuous_profiler.py +1 -30
- sentry_sdk/profiler/transaction_profiler.py +5 -56
- sentry_sdk/scope.py +108 -361
- sentry_sdk/sessions.py +0 -87
- sentry_sdk/tracing.py +415 -1161
- sentry_sdk/tracing_utils.py +130 -166
- sentry_sdk/transport.py +4 -104
- sentry_sdk/utils.py +169 -152
- {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/METADATA +3 -5
- sentry_sdk-3.0.0a2.dist-info/RECORD +154 -0
- sentry_sdk-3.0.0a2.dist-info/entry_points.txt +2 -0
- sentry_sdk/hub.py +0 -739
- sentry_sdk/integrations/opentelemetry/__init__.py +0 -7
- sentry_sdk/integrations/opentelemetry/consts.py +0 -5
- sentry_sdk/integrations/opentelemetry/integration.py +0 -58
- sentry_sdk/integrations/opentelemetry/span_processor.py +0 -391
- sentry_sdk/metrics.py +0 -965
- sentry_sdk-2.30.0.dist-info/RECORD +0 -152
- sentry_sdk-2.30.0.dist-info/entry_points.txt +0 -2
- {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/WHEEL +0 -0
- {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/licenses/LICENSE +0 -0
- {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/top_level.txt +0 -0
sentry_sdk/tracing_utils.py
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import contextlib
|
|
2
|
+
import decimal
|
|
2
3
|
import inspect
|
|
3
4
|
import os
|
|
4
5
|
import re
|
|
5
6
|
import sys
|
|
7
|
+
import uuid
|
|
6
8
|
from collections.abc import Mapping
|
|
7
|
-
from datetime import timedelta
|
|
9
|
+
from datetime import datetime, timedelta, timezone
|
|
8
10
|
from decimal import ROUND_DOWN, Decimal, DefaultContext, localcontext
|
|
9
11
|
from functools import wraps
|
|
10
12
|
from random import Random
|
|
11
13
|
from urllib.parse import quote, unquote
|
|
12
|
-
import uuid
|
|
13
14
|
|
|
14
15
|
import sentry_sdk
|
|
15
|
-
from sentry_sdk.consts import
|
|
16
|
+
from sentry_sdk.consts import (
|
|
17
|
+
OP,
|
|
18
|
+
SPANDATA,
|
|
19
|
+
SPANSTATUS,
|
|
20
|
+
BAGGAGE_HEADER_NAME,
|
|
21
|
+
SENTRY_TRACE_HEADER_NAME,
|
|
22
|
+
)
|
|
16
23
|
from sentry_sdk.utils import (
|
|
17
24
|
capture_internal_exceptions,
|
|
18
25
|
filename_for_module,
|
|
@@ -21,7 +28,6 @@ from sentry_sdk.utils import (
|
|
|
21
28
|
match_regex_list,
|
|
22
29
|
qualname_from_function,
|
|
23
30
|
to_string,
|
|
24
|
-
try_convert,
|
|
25
31
|
is_sentry_url,
|
|
26
32
|
_is_external_source,
|
|
27
33
|
_is_in_project_root,
|
|
@@ -36,7 +42,6 @@ if TYPE_CHECKING:
|
|
|
36
42
|
from typing import Generator
|
|
37
43
|
from typing import Optional
|
|
38
44
|
from typing import Union
|
|
39
|
-
|
|
40
45
|
from types import FrameType
|
|
41
46
|
|
|
42
47
|
|
|
@@ -96,17 +101,14 @@ def has_tracing_enabled(options):
|
|
|
96
101
|
# type: (Optional[Dict[str, Any]]) -> bool
|
|
97
102
|
"""
|
|
98
103
|
Returns True if either traces_sample_rate or traces_sampler is
|
|
99
|
-
defined
|
|
104
|
+
defined.
|
|
100
105
|
"""
|
|
101
106
|
if options is None:
|
|
102
107
|
return False
|
|
103
108
|
|
|
104
109
|
return bool(
|
|
105
|
-
options.get("
|
|
106
|
-
|
|
107
|
-
options.get("traces_sample_rate") is not None
|
|
108
|
-
or options.get("traces_sampler") is not None
|
|
109
|
-
)
|
|
110
|
+
options.get("traces_sample_rate") is not None
|
|
111
|
+
or options.get("traces_sampler") is not None
|
|
110
112
|
)
|
|
111
113
|
|
|
112
114
|
|
|
@@ -118,7 +120,7 @@ def record_sql_queries(
|
|
|
118
120
|
paramstyle, # type: Optional[str]
|
|
119
121
|
executemany, # type: bool
|
|
120
122
|
record_cursor_repr=False, # type: bool
|
|
121
|
-
span_origin=
|
|
123
|
+
span_origin=None, # type: Optional[str]
|
|
122
124
|
):
|
|
123
125
|
# type: (...) -> Generator[sentry_sdk.tracing.Span, None, None]
|
|
124
126
|
|
|
@@ -152,44 +154,13 @@ def record_sql_queries(
|
|
|
152
154
|
op=OP.DB,
|
|
153
155
|
name=query,
|
|
154
156
|
origin=span_origin,
|
|
157
|
+
only_if_parent=True,
|
|
155
158
|
) as span:
|
|
156
159
|
for k, v in data.items():
|
|
157
|
-
span.
|
|
160
|
+
span.set_attribute(k, v)
|
|
158
161
|
yield span
|
|
159
162
|
|
|
160
163
|
|
|
161
|
-
def maybe_create_breadcrumbs_from_span(scope, span):
|
|
162
|
-
# type: (sentry_sdk.Scope, sentry_sdk.tracing.Span) -> None
|
|
163
|
-
if span.op == OP.DB_REDIS:
|
|
164
|
-
scope.add_breadcrumb(
|
|
165
|
-
message=span.description, type="redis", category="redis", data=span._tags
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
elif span.op == OP.HTTP_CLIENT:
|
|
169
|
-
level = None
|
|
170
|
-
status_code = span._data.get(SPANDATA.HTTP_STATUS_CODE)
|
|
171
|
-
if status_code:
|
|
172
|
-
if 500 <= status_code <= 599:
|
|
173
|
-
level = "error"
|
|
174
|
-
elif 400 <= status_code <= 499:
|
|
175
|
-
level = "warning"
|
|
176
|
-
|
|
177
|
-
if level:
|
|
178
|
-
scope.add_breadcrumb(
|
|
179
|
-
type="http", category="httplib", data=span._data, level=level
|
|
180
|
-
)
|
|
181
|
-
else:
|
|
182
|
-
scope.add_breadcrumb(type="http", category="httplib", data=span._data)
|
|
183
|
-
|
|
184
|
-
elif span.op == "subprocess":
|
|
185
|
-
scope.add_breadcrumb(
|
|
186
|
-
type="subprocess",
|
|
187
|
-
category="subprocess",
|
|
188
|
-
message=span.description,
|
|
189
|
-
data=span._data,
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
|
|
193
164
|
def _get_frame_module_abs_path(frame):
|
|
194
165
|
# type: (FrameType) -> Optional[str]
|
|
195
166
|
try:
|
|
@@ -227,14 +198,17 @@ def add_query_source(span):
|
|
|
227
198
|
if not client.is_active():
|
|
228
199
|
return
|
|
229
200
|
|
|
230
|
-
if span.
|
|
201
|
+
if span.start_timestamp is None:
|
|
231
202
|
return
|
|
232
203
|
|
|
233
204
|
should_add_query_source = client.options.get("enable_db_query_source", True)
|
|
234
205
|
if not should_add_query_source:
|
|
235
206
|
return
|
|
236
207
|
|
|
237
|
-
|
|
208
|
+
# We assume here that the query is just ending now. We can't use
|
|
209
|
+
# the actual end timestamp of the span because in OTel the span
|
|
210
|
+
# can't be finished in order to set any attributes on it.
|
|
211
|
+
duration = datetime.now(tz=timezone.utc) - span.start_timestamp
|
|
238
212
|
threshold = client.options.get("db_query_source_threshold_ms", 0)
|
|
239
213
|
slow_query = duration / timedelta(milliseconds=1) > threshold
|
|
240
214
|
|
|
@@ -281,14 +255,14 @@ def add_query_source(span):
|
|
|
281
255
|
except Exception:
|
|
282
256
|
lineno = None
|
|
283
257
|
if lineno is not None:
|
|
284
|
-
span.
|
|
258
|
+
span.set_attribute(SPANDATA.CODE_LINENO, frame.f_lineno)
|
|
285
259
|
|
|
286
260
|
try:
|
|
287
261
|
namespace = frame.f_globals.get("__name__")
|
|
288
262
|
except Exception:
|
|
289
263
|
namespace = None
|
|
290
264
|
if namespace is not None:
|
|
291
|
-
span.
|
|
265
|
+
span.set_attribute(SPANDATA.CODE_NAMESPACE, namespace)
|
|
292
266
|
|
|
293
267
|
filepath = _get_frame_module_abs_path(frame)
|
|
294
268
|
if filepath is not None:
|
|
@@ -298,7 +272,7 @@ def add_query_source(span):
|
|
|
298
272
|
in_app_path = filepath.replace(project_root, "").lstrip(os.sep)
|
|
299
273
|
else:
|
|
300
274
|
in_app_path = filepath
|
|
301
|
-
span.
|
|
275
|
+
span.set_attribute(SPANDATA.CODE_FILEPATH, in_app_path)
|
|
302
276
|
|
|
303
277
|
try:
|
|
304
278
|
code_function = frame.f_code.co_name
|
|
@@ -306,7 +280,7 @@ def add_query_source(span):
|
|
|
306
280
|
code_function = None
|
|
307
281
|
|
|
308
282
|
if code_function is not None:
|
|
309
|
-
span.
|
|
283
|
+
span.set_attribute(SPANDATA.CODE_FUNCTION, frame.f_code.co_name)
|
|
310
284
|
|
|
311
285
|
|
|
312
286
|
def extract_sentrytrace_data(header):
|
|
@@ -371,7 +345,7 @@ class PropagationContext:
|
|
|
371
345
|
"_span_id",
|
|
372
346
|
"parent_span_id",
|
|
373
347
|
"parent_sampled",
|
|
374
|
-
"
|
|
348
|
+
"baggage",
|
|
375
349
|
)
|
|
376
350
|
|
|
377
351
|
def __init__(
|
|
@@ -380,7 +354,7 @@ class PropagationContext:
|
|
|
380
354
|
span_id=None, # type: Optional[str]
|
|
381
355
|
parent_span_id=None, # type: Optional[str]
|
|
382
356
|
parent_sampled=None, # type: Optional[bool]
|
|
383
|
-
|
|
357
|
+
baggage=None, # type: Optional[Baggage]
|
|
384
358
|
):
|
|
385
359
|
# type: (...) -> None
|
|
386
360
|
self._trace_id = trace_id
|
|
@@ -398,8 +372,13 @@ class PropagationContext:
|
|
|
398
372
|
Important when the parent span originated in an upstream service,
|
|
399
373
|
because we want to sample the whole trace, or nothing from the trace."""
|
|
400
374
|
|
|
401
|
-
self.
|
|
402
|
-
"""
|
|
375
|
+
self.baggage = baggage
|
|
376
|
+
"""Baggage object used for dynamic sampling decisions."""
|
|
377
|
+
|
|
378
|
+
@property
|
|
379
|
+
def dynamic_sampling_context(self):
|
|
380
|
+
# type: () -> Optional[Dict[str, str]]
|
|
381
|
+
return self.baggage.dynamic_sampling_context() if self.baggage else None
|
|
403
382
|
|
|
404
383
|
@classmethod
|
|
405
384
|
def from_incoming_data(cls, incoming_data):
|
|
@@ -410,9 +389,7 @@ class PropagationContext:
|
|
|
410
389
|
baggage_header = normalized_data.get(BAGGAGE_HEADER_NAME)
|
|
411
390
|
if baggage_header:
|
|
412
391
|
propagation_context = PropagationContext()
|
|
413
|
-
propagation_context.
|
|
414
|
-
baggage_header
|
|
415
|
-
).dynamic_sampling_context()
|
|
392
|
+
propagation_context.baggage = Baggage.from_incoming_header(baggage_header)
|
|
416
393
|
|
|
417
394
|
sentry_trace_header = normalized_data.get(SENTRY_TRACE_HEADER_NAME)
|
|
418
395
|
if sentry_trace_header:
|
|
@@ -432,7 +409,6 @@ class PropagationContext:
|
|
|
432
409
|
# type: () -> str
|
|
433
410
|
"""The trace id of the Sentry trace."""
|
|
434
411
|
if not self._trace_id:
|
|
435
|
-
# New trace, don't fill in sample_rand
|
|
436
412
|
self._trace_id = uuid.uuid4().hex
|
|
437
413
|
|
|
438
414
|
return self._trace_id
|
|
@@ -456,6 +432,21 @@ class PropagationContext:
|
|
|
456
432
|
# type: (str) -> None
|
|
457
433
|
self._span_id = value
|
|
458
434
|
|
|
435
|
+
def to_traceparent(self):
|
|
436
|
+
# type: () -> str
|
|
437
|
+
if self.parent_sampled is True:
|
|
438
|
+
sampled = "1"
|
|
439
|
+
elif self.parent_sampled is False:
|
|
440
|
+
sampled = "0"
|
|
441
|
+
else:
|
|
442
|
+
sampled = None
|
|
443
|
+
|
|
444
|
+
traceparent = "%s-%s" % (self.trace_id, self.span_id)
|
|
445
|
+
if sampled is not None:
|
|
446
|
+
traceparent += "-%s" % (sampled,)
|
|
447
|
+
|
|
448
|
+
return traceparent
|
|
449
|
+
|
|
459
450
|
def update(self, other_dict):
|
|
460
451
|
# type: (Dict[str, Any]) -> None
|
|
461
452
|
"""
|
|
@@ -467,22 +458,12 @@ class PropagationContext:
|
|
|
467
458
|
except AttributeError:
|
|
468
459
|
pass
|
|
469
460
|
|
|
470
|
-
def __repr__(self):
|
|
471
|
-
# type: (...) -> str
|
|
472
|
-
return "<PropagationContext _trace_id={} _span_id={} parent_span_id={} parent_sampled={} dynamic_sampling_context={}>".format(
|
|
473
|
-
self._trace_id,
|
|
474
|
-
self._span_id,
|
|
475
|
-
self.parent_span_id,
|
|
476
|
-
self.parent_sampled,
|
|
477
|
-
self.dynamic_sampling_context,
|
|
478
|
-
)
|
|
479
|
-
|
|
480
461
|
def _fill_sample_rand(self):
|
|
481
462
|
# type: () -> None
|
|
482
463
|
"""
|
|
483
|
-
Ensure that there is a valid sample_rand value in the
|
|
464
|
+
Ensure that there is a valid sample_rand value in the baggage.
|
|
484
465
|
|
|
485
|
-
If there is a valid sample_rand value in the
|
|
466
|
+
If there is a valid sample_rand value in the baggage, we keep it.
|
|
486
467
|
Otherwise, we generate a sample_rand value according to the following:
|
|
487
468
|
|
|
488
469
|
- If we have a parent_sampled value and a sample_rate in the DSC, we compute
|
|
@@ -497,21 +478,33 @@ class PropagationContext:
|
|
|
497
478
|
|
|
498
479
|
This function does nothing if there is no dynamic_sampling_context.
|
|
499
480
|
"""
|
|
500
|
-
if self.dynamic_sampling_context is None:
|
|
481
|
+
if self.dynamic_sampling_context is None or self.baggage is None:
|
|
501
482
|
return
|
|
502
483
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
484
|
+
sentry_baggage = self.baggage.sentry_items
|
|
485
|
+
|
|
486
|
+
sample_rand = None
|
|
487
|
+
if sentry_baggage.get("sample_rand"):
|
|
488
|
+
try:
|
|
489
|
+
sample_rand = Decimal(sentry_baggage["sample_rand"])
|
|
490
|
+
except Exception:
|
|
491
|
+
logger.debug(
|
|
492
|
+
f"Failed to convert incoming sample_rand to Decimal: {sample_rand}"
|
|
493
|
+
)
|
|
494
|
+
|
|
506
495
|
if sample_rand is not None and 0 <= sample_rand < 1:
|
|
507
496
|
# sample_rand is present and valid, so don't overwrite it
|
|
508
497
|
return
|
|
509
498
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
499
|
+
sample_rate = None
|
|
500
|
+
if sentry_baggage.get("sample_rate"):
|
|
501
|
+
try:
|
|
502
|
+
sample_rate = float(sentry_baggage["sample_rate"])
|
|
503
|
+
except Exception:
|
|
504
|
+
logger.debug(
|
|
505
|
+
f"Failed to convert incoming sample_rate to float: {sample_rate}"
|
|
506
|
+
)
|
|
507
|
+
|
|
515
508
|
lower, upper = _sample_rand_range(self.parent_sampled, sample_rate)
|
|
516
509
|
|
|
517
510
|
try:
|
|
@@ -527,17 +520,26 @@ class PropagationContext:
|
|
|
527
520
|
)
|
|
528
521
|
return
|
|
529
522
|
|
|
530
|
-
self.
|
|
531
|
-
f"{sample_rand:.6f}" # noqa: E231
|
|
532
|
-
)
|
|
523
|
+
self.baggage.sentry_items["sample_rand"] = f"{sample_rand:.6f}" # noqa: E231
|
|
533
524
|
|
|
534
525
|
def _sample_rand(self):
|
|
535
526
|
# type: () -> Optional[str]
|
|
536
|
-
"""Convenience method to get the sample_rand value from the
|
|
537
|
-
if self.
|
|
527
|
+
"""Convenience method to get the sample_rand value from the baggage."""
|
|
528
|
+
if self.baggage is None:
|
|
538
529
|
return None
|
|
539
530
|
|
|
540
|
-
return self.
|
|
531
|
+
return self.baggage.sentry_items.get("sample_rand")
|
|
532
|
+
|
|
533
|
+
def __repr__(self):
|
|
534
|
+
# type: (...) -> str
|
|
535
|
+
return "<PropagationContext _trace_id={} _span_id={} parent_span_id={} parent_sampled={} baggage={} dynamic_sampling_context={}>".format(
|
|
536
|
+
self._trace_id,
|
|
537
|
+
self._span_id,
|
|
538
|
+
self.parent_span_id,
|
|
539
|
+
self.parent_sampled,
|
|
540
|
+
self.baggage,
|
|
541
|
+
self.dynamic_sampling_context,
|
|
542
|
+
)
|
|
541
543
|
|
|
542
544
|
|
|
543
545
|
class Baggage:
|
|
@@ -568,8 +570,6 @@ class Baggage:
|
|
|
568
570
|
def from_incoming_header(
|
|
569
571
|
cls,
|
|
570
572
|
header, # type: Optional[str]
|
|
571
|
-
*,
|
|
572
|
-
_sample_rand=None, # type: Optional[str]
|
|
573
573
|
):
|
|
574
574
|
# type: (...) -> Baggage
|
|
575
575
|
"""
|
|
@@ -594,10 +594,6 @@ class Baggage:
|
|
|
594
594
|
else:
|
|
595
595
|
third_party_items += ("," if third_party_items else "") + item
|
|
596
596
|
|
|
597
|
-
if _sample_rand is not None:
|
|
598
|
-
sentry_items["sample_rand"] = str(_sample_rand)
|
|
599
|
-
mutable = False
|
|
600
|
-
|
|
601
597
|
return Baggage(sentry_items, third_party_items, mutable)
|
|
602
598
|
|
|
603
599
|
@classmethod
|
|
@@ -633,53 +629,6 @@ class Baggage:
|
|
|
633
629
|
|
|
634
630
|
return Baggage(sentry_items, third_party_items, mutable)
|
|
635
631
|
|
|
636
|
-
@classmethod
|
|
637
|
-
def populate_from_transaction(cls, transaction):
|
|
638
|
-
# type: (sentry_sdk.tracing.Transaction) -> Baggage
|
|
639
|
-
"""
|
|
640
|
-
Populate fresh baggage entry with sentry_items and make it immutable
|
|
641
|
-
if this is the head SDK which originates traces.
|
|
642
|
-
"""
|
|
643
|
-
client = sentry_sdk.get_client()
|
|
644
|
-
sentry_items = {} # type: Dict[str, str]
|
|
645
|
-
|
|
646
|
-
if not client.is_active():
|
|
647
|
-
return Baggage(sentry_items)
|
|
648
|
-
|
|
649
|
-
options = client.options or {}
|
|
650
|
-
|
|
651
|
-
sentry_items["trace_id"] = transaction.trace_id
|
|
652
|
-
sentry_items["sample_rand"] = str(transaction._sample_rand)
|
|
653
|
-
|
|
654
|
-
if options.get("environment"):
|
|
655
|
-
sentry_items["environment"] = options["environment"]
|
|
656
|
-
|
|
657
|
-
if options.get("release"):
|
|
658
|
-
sentry_items["release"] = options["release"]
|
|
659
|
-
|
|
660
|
-
if options.get("dsn"):
|
|
661
|
-
sentry_items["public_key"] = Dsn(options["dsn"]).public_key
|
|
662
|
-
|
|
663
|
-
if (
|
|
664
|
-
transaction.name
|
|
665
|
-
and transaction.source not in LOW_QUALITY_TRANSACTION_SOURCES
|
|
666
|
-
):
|
|
667
|
-
sentry_items["transaction"] = transaction.name
|
|
668
|
-
|
|
669
|
-
if transaction.sample_rate is not None:
|
|
670
|
-
sentry_items["sample_rate"] = str(transaction.sample_rate)
|
|
671
|
-
|
|
672
|
-
if transaction.sampled is not None:
|
|
673
|
-
sentry_items["sampled"] = "true" if transaction.sampled else "false"
|
|
674
|
-
|
|
675
|
-
# there's an existing baggage but it was mutable,
|
|
676
|
-
# which is why we are creating this new baggage.
|
|
677
|
-
# However, if by chance the user put some sentry items in there, give them precedence.
|
|
678
|
-
if transaction._baggage and transaction._baggage.sentry_items:
|
|
679
|
-
sentry_items.update(transaction._baggage.sentry_items)
|
|
680
|
-
|
|
681
|
-
return Baggage(sentry_items, mutable=False)
|
|
682
|
-
|
|
683
632
|
def freeze(self):
|
|
684
633
|
# type: () -> None
|
|
685
634
|
self.mutable = False
|
|
@@ -722,20 +671,6 @@ class Baggage:
|
|
|
722
671
|
)
|
|
723
672
|
)
|
|
724
673
|
|
|
725
|
-
def _sample_rand(self):
|
|
726
|
-
# type: () -> Optional[Decimal]
|
|
727
|
-
"""Convenience method to get the sample_rand value from the sentry_items.
|
|
728
|
-
|
|
729
|
-
We validate the value and parse it as a Decimal before returning it. The value is considered
|
|
730
|
-
valid if it is a Decimal in the range [0, 1).
|
|
731
|
-
"""
|
|
732
|
-
sample_rand = try_convert(Decimal, self.sentry_items.get("sample_rand"))
|
|
733
|
-
|
|
734
|
-
if sample_rand is not None and Decimal(0) <= sample_rand < Decimal(1):
|
|
735
|
-
return sample_rand
|
|
736
|
-
|
|
737
|
-
return None
|
|
738
|
-
|
|
739
674
|
def __repr__(self):
|
|
740
675
|
# type: () -> str
|
|
741
676
|
return f'<Baggage "{self.serialize(include_third_party=True)}", mutable={self.mutable}>'
|
|
@@ -794,9 +729,10 @@ def start_child_span_decorator(func):
|
|
|
794
729
|
)
|
|
795
730
|
return await func(*args, **kwargs)
|
|
796
731
|
|
|
797
|
-
with
|
|
732
|
+
with sentry_sdk.start_span(
|
|
798
733
|
op=OP.FUNCTION,
|
|
799
734
|
name=qualname_from_function(func),
|
|
735
|
+
only_if_parent=True,
|
|
800
736
|
):
|
|
801
737
|
return await func(*args, **kwargs)
|
|
802
738
|
|
|
@@ -822,9 +758,10 @@ def start_child_span_decorator(func):
|
|
|
822
758
|
)
|
|
823
759
|
return func(*args, **kwargs)
|
|
824
760
|
|
|
825
|
-
with
|
|
761
|
+
with sentry_sdk.start_span(
|
|
826
762
|
op=OP.FUNCTION,
|
|
827
763
|
name=qualname_from_function(func),
|
|
764
|
+
only_if_parent=True,
|
|
828
765
|
):
|
|
829
766
|
return func(*args, **kwargs)
|
|
830
767
|
|
|
@@ -837,7 +774,7 @@ def start_child_span_decorator(func):
|
|
|
837
774
|
|
|
838
775
|
|
|
839
776
|
def get_current_span(scope=None):
|
|
840
|
-
# type: (Optional[sentry_sdk.Scope]) -> Optional[Span]
|
|
777
|
+
# type: (Optional[sentry_sdk.Scope]) -> Optional[sentry_sdk.tracing.Span]
|
|
841
778
|
"""
|
|
842
779
|
Returns the currently active span if there is one running, otherwise `None`
|
|
843
780
|
"""
|
|
@@ -848,10 +785,9 @@ def get_current_span(scope=None):
|
|
|
848
785
|
|
|
849
786
|
def _generate_sample_rand(
|
|
850
787
|
trace_id, # type: Optional[str]
|
|
851
|
-
*,
|
|
852
788
|
interval=(0.0, 1.0), # type: tuple[float, float]
|
|
853
789
|
):
|
|
854
|
-
# type: (...) -> Decimal
|
|
790
|
+
# type: (...) -> Optional[decimal.Decimal]
|
|
855
791
|
"""Generate a sample_rand value from a trace ID.
|
|
856
792
|
|
|
857
793
|
The generated value will be pseudorandomly chosen from the provided
|
|
@@ -896,12 +832,40 @@ def _sample_rand_range(parent_sampled, sample_rate):
|
|
|
896
832
|
return sample_rate, 1.0
|
|
897
833
|
|
|
898
834
|
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
SENTRY_TRACE_HEADER_NAME,
|
|
904
|
-
)
|
|
835
|
+
def get_span_status_from_http_code(http_status_code):
|
|
836
|
+
# type: (int) -> str
|
|
837
|
+
"""
|
|
838
|
+
Returns the Sentry status corresponding to the given HTTP status code.
|
|
905
839
|
|
|
906
|
-
|
|
907
|
-
|
|
840
|
+
See: https://develop.sentry.dev/sdk/event-payloads/contexts/#trace-context
|
|
841
|
+
"""
|
|
842
|
+
if http_status_code < 400:
|
|
843
|
+
return SPANSTATUS.OK
|
|
844
|
+
|
|
845
|
+
elif 400 <= http_status_code < 500:
|
|
846
|
+
if http_status_code == 403:
|
|
847
|
+
return SPANSTATUS.PERMISSION_DENIED
|
|
848
|
+
elif http_status_code == 404:
|
|
849
|
+
return SPANSTATUS.NOT_FOUND
|
|
850
|
+
elif http_status_code == 429:
|
|
851
|
+
return SPANSTATUS.RESOURCE_EXHAUSTED
|
|
852
|
+
elif http_status_code == 413:
|
|
853
|
+
return SPANSTATUS.FAILED_PRECONDITION
|
|
854
|
+
elif http_status_code == 401:
|
|
855
|
+
return SPANSTATUS.UNAUTHENTICATED
|
|
856
|
+
elif http_status_code == 409:
|
|
857
|
+
return SPANSTATUS.ALREADY_EXISTS
|
|
858
|
+
else:
|
|
859
|
+
return SPANSTATUS.INVALID_ARGUMENT
|
|
860
|
+
|
|
861
|
+
elif 500 <= http_status_code < 600:
|
|
862
|
+
if http_status_code == 504:
|
|
863
|
+
return SPANSTATUS.DEADLINE_EXCEEDED
|
|
864
|
+
elif http_status_code == 501:
|
|
865
|
+
return SPANSTATUS.UNIMPLEMENTED
|
|
866
|
+
elif http_status_code == 503:
|
|
867
|
+
return SPANSTATUS.UNAVAILABLE
|
|
868
|
+
else:
|
|
869
|
+
return SPANSTATUS.INTERNAL_ERROR
|
|
870
|
+
|
|
871
|
+
return SPANSTATUS.UNKNOWN_ERROR
|