sentry-sdk 2.26.1__py2.py3-none-any.whl → 3.0.0a1__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.

Files changed (114) hide show
  1. sentry_sdk/__init__.py +4 -8
  2. sentry_sdk/_compat.py +0 -1
  3. sentry_sdk/_init_implementation.py +6 -44
  4. sentry_sdk/_log_batcher.py +47 -28
  5. sentry_sdk/_types.py +8 -64
  6. sentry_sdk/ai/monitoring.py +14 -10
  7. sentry_sdk/ai/utils.py +1 -1
  8. sentry_sdk/api.py +69 -163
  9. sentry_sdk/client.py +25 -72
  10. sentry_sdk/consts.py +42 -23
  11. sentry_sdk/debug.py +0 -10
  12. sentry_sdk/envelope.py +2 -10
  13. sentry_sdk/feature_flags.py +5 -1
  14. sentry_sdk/integrations/__init__.py +5 -2
  15. sentry_sdk/integrations/_asgi_common.py +3 -3
  16. sentry_sdk/integrations/_wsgi_common.py +11 -40
  17. sentry_sdk/integrations/aiohttp.py +104 -57
  18. sentry_sdk/integrations/anthropic.py +10 -7
  19. sentry_sdk/integrations/arq.py +24 -13
  20. sentry_sdk/integrations/asgi.py +103 -83
  21. sentry_sdk/integrations/asyncio.py +1 -0
  22. sentry_sdk/integrations/asyncpg.py +45 -30
  23. sentry_sdk/integrations/aws_lambda.py +109 -92
  24. sentry_sdk/integrations/boto3.py +38 -9
  25. sentry_sdk/integrations/bottle.py +1 -1
  26. sentry_sdk/integrations/celery/__init__.py +48 -38
  27. sentry_sdk/integrations/clickhouse_driver.py +59 -28
  28. sentry_sdk/integrations/cohere.py +2 -0
  29. sentry_sdk/integrations/django/__init__.py +25 -46
  30. sentry_sdk/integrations/django/asgi.py +6 -2
  31. sentry_sdk/integrations/django/caching.py +13 -22
  32. sentry_sdk/integrations/django/middleware.py +1 -0
  33. sentry_sdk/integrations/django/signals_handlers.py +3 -1
  34. sentry_sdk/integrations/django/templates.py +8 -12
  35. sentry_sdk/integrations/django/transactions.py +1 -6
  36. sentry_sdk/integrations/django/views.py +5 -2
  37. sentry_sdk/integrations/falcon.py +7 -25
  38. sentry_sdk/integrations/fastapi.py +3 -3
  39. sentry_sdk/integrations/flask.py +1 -1
  40. sentry_sdk/integrations/gcp.py +63 -38
  41. sentry_sdk/integrations/graphene.py +6 -13
  42. sentry_sdk/integrations/grpc/aio/client.py +14 -8
  43. sentry_sdk/integrations/grpc/aio/server.py +19 -21
  44. sentry_sdk/integrations/grpc/client.py +8 -6
  45. sentry_sdk/integrations/grpc/server.py +12 -14
  46. sentry_sdk/integrations/httpx.py +47 -12
  47. sentry_sdk/integrations/huey.py +26 -22
  48. sentry_sdk/integrations/huggingface_hub.py +1 -0
  49. sentry_sdk/integrations/langchain.py +22 -15
  50. sentry_sdk/integrations/launchdarkly.py +3 -3
  51. sentry_sdk/integrations/litestar.py +4 -2
  52. sentry_sdk/integrations/logging.py +12 -3
  53. sentry_sdk/integrations/openai.py +2 -0
  54. sentry_sdk/integrations/openfeature.py +3 -5
  55. sentry_sdk/integrations/pymongo.py +18 -25
  56. sentry_sdk/integrations/pyramid.py +1 -1
  57. sentry_sdk/integrations/quart.py +3 -3
  58. sentry_sdk/integrations/ray.py +23 -17
  59. sentry_sdk/integrations/redis/_async_common.py +30 -18
  60. sentry_sdk/integrations/redis/_sync_common.py +28 -18
  61. sentry_sdk/integrations/redis/modules/caches.py +13 -10
  62. sentry_sdk/integrations/redis/modules/queries.py +14 -11
  63. sentry_sdk/integrations/redis/rb.py +4 -4
  64. sentry_sdk/integrations/redis/redis.py +6 -6
  65. sentry_sdk/integrations/redis/redis_cluster.py +18 -16
  66. sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +4 -4
  67. sentry_sdk/integrations/redis/utils.py +63 -19
  68. sentry_sdk/integrations/rq.py +68 -23
  69. sentry_sdk/integrations/rust_tracing.py +28 -43
  70. sentry_sdk/integrations/sanic.py +23 -13
  71. sentry_sdk/integrations/socket.py +9 -5
  72. sentry_sdk/integrations/sqlalchemy.py +8 -8
  73. sentry_sdk/integrations/starlette.py +11 -31
  74. sentry_sdk/integrations/starlite.py +4 -2
  75. sentry_sdk/integrations/stdlib.py +56 -9
  76. sentry_sdk/integrations/strawberry.py +40 -59
  77. sentry_sdk/integrations/threading.py +10 -26
  78. sentry_sdk/integrations/tornado.py +57 -18
  79. sentry_sdk/integrations/trytond.py +4 -1
  80. sentry_sdk/integrations/unleash.py +2 -3
  81. sentry_sdk/integrations/wsgi.py +84 -38
  82. sentry_sdk/opentelemetry/__init__.py +9 -0
  83. sentry_sdk/opentelemetry/consts.py +33 -0
  84. sentry_sdk/opentelemetry/contextvars_context.py +73 -0
  85. sentry_sdk/{integrations/opentelemetry → opentelemetry}/propagator.py +19 -28
  86. sentry_sdk/opentelemetry/sampler.py +326 -0
  87. sentry_sdk/opentelemetry/scope.py +218 -0
  88. sentry_sdk/opentelemetry/span_processor.py +329 -0
  89. sentry_sdk/opentelemetry/tracing.py +35 -0
  90. sentry_sdk/opentelemetry/utils.py +476 -0
  91. sentry_sdk/profiler/__init__.py +0 -40
  92. sentry_sdk/profiler/continuous_profiler.py +1 -30
  93. sentry_sdk/profiler/transaction_profiler.py +5 -56
  94. sentry_sdk/scope.py +107 -351
  95. sentry_sdk/sessions.py +0 -87
  96. sentry_sdk/tracing.py +418 -1134
  97. sentry_sdk/tracing_utils.py +134 -169
  98. sentry_sdk/transport.py +4 -104
  99. sentry_sdk/types.py +26 -2
  100. sentry_sdk/utils.py +169 -152
  101. {sentry_sdk-2.26.1.dist-info → sentry_sdk-3.0.0a1.dist-info}/METADATA +3 -5
  102. sentry_sdk-3.0.0a1.dist-info/RECORD +154 -0
  103. {sentry_sdk-2.26.1.dist-info → sentry_sdk-3.0.0a1.dist-info}/WHEEL +1 -1
  104. sentry_sdk-3.0.0a1.dist-info/entry_points.txt +2 -0
  105. sentry_sdk/hub.py +0 -739
  106. sentry_sdk/integrations/opentelemetry/__init__.py +0 -7
  107. sentry_sdk/integrations/opentelemetry/consts.py +0 -5
  108. sentry_sdk/integrations/opentelemetry/integration.py +0 -58
  109. sentry_sdk/integrations/opentelemetry/span_processor.py +0 -391
  110. sentry_sdk/metrics.py +0 -965
  111. sentry_sdk-2.26.1.dist-info/RECORD +0 -152
  112. sentry_sdk-2.26.1.dist-info/entry_points.txt +0 -2
  113. {sentry_sdk-2.26.1.dist-info → sentry_sdk-3.0.0a1.dist-info}/licenses/LICENSE +0 -0
  114. {sentry_sdk-2.26.1.dist-info → sentry_sdk-3.0.0a1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,326 @@
1
+ from decimal import Decimal
2
+ from typing import cast
3
+
4
+ from opentelemetry import trace
5
+ from opentelemetry.sdk.trace.sampling import Sampler, SamplingResult, Decision
6
+ from opentelemetry.trace.span import TraceState
7
+
8
+ import sentry_sdk
9
+ from sentry_sdk.opentelemetry.consts import (
10
+ TRACESTATE_SAMPLED_KEY,
11
+ TRACESTATE_SAMPLE_RAND_KEY,
12
+ TRACESTATE_SAMPLE_RATE_KEY,
13
+ SentrySpanAttribute,
14
+ )
15
+ from sentry_sdk.tracing_utils import (
16
+ _generate_sample_rand,
17
+ has_tracing_enabled,
18
+ )
19
+ from sentry_sdk.utils import is_valid_sample_rate, logger
20
+
21
+ from typing import TYPE_CHECKING
22
+
23
+ if TYPE_CHECKING:
24
+ from typing import Any, Optional, Sequence, Union
25
+ from opentelemetry.context import Context
26
+ from opentelemetry.trace import Link, SpanKind
27
+ from opentelemetry.trace.span import SpanContext
28
+ from opentelemetry.util.types import Attributes
29
+
30
+
31
+ def get_parent_sampled(parent_context, trace_id):
32
+ # type: (Optional[SpanContext], int) -> Optional[bool]
33
+ if parent_context is None:
34
+ return None
35
+
36
+ is_span_context_valid = parent_context is not None and parent_context.is_valid
37
+
38
+ # Only inherit sample rate if `traceId` is the same
39
+ if is_span_context_valid and parent_context.trace_id == trace_id:
40
+ # this is getSamplingDecision in JS
41
+ # if there was no sampling flag, defer the decision
42
+ dsc_sampled = parent_context.trace_state.get(TRACESTATE_SAMPLED_KEY)
43
+ if dsc_sampled == "deferred":
44
+ return None
45
+
46
+ if parent_context.trace_flags.sampled is not None:
47
+ return parent_context.trace_flags.sampled
48
+
49
+ if dsc_sampled == "true":
50
+ return True
51
+ elif dsc_sampled == "false":
52
+ return False
53
+
54
+ return None
55
+
56
+
57
+ def get_parent_sample_rate(parent_context, trace_id):
58
+ # type: (Optional[SpanContext], int) -> Optional[float]
59
+ if parent_context is None:
60
+ return None
61
+
62
+ is_span_context_valid = parent_context is not None and parent_context.is_valid
63
+
64
+ if is_span_context_valid and parent_context.trace_id == trace_id:
65
+ parent_sample_rate = parent_context.trace_state.get(TRACESTATE_SAMPLE_RATE_KEY)
66
+ if parent_sample_rate is None:
67
+ return None
68
+
69
+ try:
70
+ return float(parent_sample_rate)
71
+ except Exception:
72
+ return None
73
+
74
+ return None
75
+
76
+
77
+ def get_parent_sample_rand(parent_context, trace_id):
78
+ # type: (Optional[SpanContext], int) -> Optional[Decimal]
79
+ if parent_context is None:
80
+ return None
81
+
82
+ is_span_context_valid = parent_context is not None and parent_context.is_valid
83
+
84
+ if is_span_context_valid and parent_context.trace_id == trace_id:
85
+ parent_sample_rand = parent_context.trace_state.get(TRACESTATE_SAMPLE_RAND_KEY)
86
+ if parent_sample_rand is None:
87
+ return None
88
+
89
+ return Decimal(parent_sample_rand)
90
+
91
+ return None
92
+
93
+
94
+ def dropped_result(span_context, attributes, sample_rate=None, sample_rand=None):
95
+ # type: (SpanContext, Attributes, Optional[float], Optional[Decimal]) -> SamplingResult
96
+ """
97
+ React to a span getting unsampled and return a DROP SamplingResult.
98
+
99
+ Update the trace_state with the effective sampled, sample_rate and sample_rand,
100
+ record that we dropped the event for client report purposes, and return
101
+ an OTel SamplingResult with Decision.DROP.
102
+
103
+ See for more info about OTel sampling:
104
+ https://opentelemetry-python.readthedocs.io/en/latest/sdk/trace.sampling.html
105
+ """
106
+ trace_state = _update_trace_state(
107
+ span_context, sampled=False, sample_rate=sample_rate, sample_rand=sample_rand
108
+ )
109
+
110
+ is_root_span = not (span_context.is_valid and not span_context.is_remote)
111
+ if is_root_span:
112
+ # Tell Sentry why we dropped the transaction/root-span
113
+ client = sentry_sdk.get_client()
114
+ if client.monitor and client.monitor.downsample_factor > 0:
115
+ reason = "backpressure"
116
+ else:
117
+ reason = "sample_rate"
118
+
119
+ if client.transport and has_tracing_enabled(client.options):
120
+ client.transport.record_lost_event(reason, data_category="transaction")
121
+
122
+ # Only one span (the transaction itself) is discarded, since we did not record any spans here.
123
+ client.transport.record_lost_event(reason, data_category="span")
124
+
125
+ return SamplingResult(
126
+ Decision.DROP,
127
+ attributes=attributes,
128
+ trace_state=trace_state,
129
+ )
130
+
131
+
132
+ def sampled_result(span_context, attributes, sample_rate=None, sample_rand=None):
133
+ # type: (SpanContext, Attributes, Optional[float], Optional[Decimal]) -> SamplingResult
134
+ """
135
+ React to a span being sampled and return a sampled SamplingResult.
136
+
137
+ Update the trace_state with the effective sampled, sample_rate and sample_rand,
138
+ and return an OTel SamplingResult with Decision.RECORD_AND_SAMPLE.
139
+
140
+ See for more info about OTel sampling:
141
+ https://opentelemetry-python.readthedocs.io/en/latest/sdk/trace.sampling.html
142
+ """
143
+ trace_state = _update_trace_state(
144
+ span_context, sampled=True, sample_rate=sample_rate, sample_rand=sample_rand
145
+ )
146
+
147
+ return SamplingResult(
148
+ Decision.RECORD_AND_SAMPLE,
149
+ attributes=attributes,
150
+ trace_state=trace_state,
151
+ )
152
+
153
+
154
+ def _update_trace_state(span_context, sampled, sample_rate=None, sample_rand=None):
155
+ # type: (SpanContext, bool, Optional[float], Optional[Decimal]) -> TraceState
156
+ trace_state = span_context.trace_state
157
+
158
+ sampled = "true" if sampled else "false"
159
+ if TRACESTATE_SAMPLED_KEY not in trace_state:
160
+ trace_state = trace_state.add(TRACESTATE_SAMPLED_KEY, sampled)
161
+ elif trace_state.get(TRACESTATE_SAMPLED_KEY) == "deferred":
162
+ trace_state = trace_state.update(TRACESTATE_SAMPLED_KEY, sampled)
163
+
164
+ if sample_rate is not None:
165
+ trace_state = trace_state.update(TRACESTATE_SAMPLE_RATE_KEY, str(sample_rate))
166
+
167
+ if sample_rand is not None:
168
+ trace_state = trace_state.update(
169
+ TRACESTATE_SAMPLE_RAND_KEY, f"{sample_rand:.6f}" # noqa: E231
170
+ )
171
+
172
+ return trace_state
173
+
174
+
175
+ class SentrySampler(Sampler):
176
+ def should_sample(
177
+ self,
178
+ parent_context, # type: Optional[Context]
179
+ trace_id, # type: int
180
+ name, # type: str
181
+ kind=None, # type: Optional[SpanKind]
182
+ attributes=None, # type: Attributes
183
+ links=None, # type: Optional[Sequence[Link]]
184
+ trace_state=None, # type: Optional[TraceState]
185
+ ):
186
+ # type: (...) -> SamplingResult
187
+ client = sentry_sdk.get_client()
188
+
189
+ parent_span_context = trace.get_current_span(parent_context).get_span_context()
190
+
191
+ attributes = attributes or {}
192
+
193
+ # No tracing enabled, thus no sampling
194
+ if not has_tracing_enabled(client.options):
195
+ return dropped_result(parent_span_context, attributes)
196
+
197
+ # parent_span_context.is_valid means this span has a parent, remote or local
198
+ is_root_span = not parent_span_context.is_valid or parent_span_context.is_remote
199
+
200
+ sample_rate = None
201
+
202
+ parent_sampled = get_parent_sampled(parent_span_context, trace_id)
203
+ parent_sample_rate = get_parent_sample_rate(parent_span_context, trace_id)
204
+ parent_sample_rand = get_parent_sample_rand(parent_span_context, trace_id)
205
+
206
+ if parent_sample_rand is not None:
207
+ # We have a sample_rand on the incoming trace or we already backfilled
208
+ # it in PropagationContext
209
+ sample_rand = parent_sample_rand
210
+ else:
211
+ # We are the head SDK and we need to generate a new sample_rand
212
+ sample_rand = cast(Decimal, _generate_sample_rand(str(trace_id), (0, 1)))
213
+
214
+ # Explicit sampled value provided at start_span
215
+ custom_sampled = cast(
216
+ "Optional[bool]", attributes.get(SentrySpanAttribute.CUSTOM_SAMPLED)
217
+ )
218
+ if custom_sampled is not None:
219
+ if is_root_span:
220
+ sample_rate = float(custom_sampled)
221
+ if sample_rate > 0:
222
+ return sampled_result(
223
+ parent_span_context,
224
+ attributes,
225
+ sample_rate=sample_rate,
226
+ sample_rand=sample_rand,
227
+ )
228
+ else:
229
+ return dropped_result(
230
+ parent_span_context,
231
+ attributes,
232
+ sample_rate=sample_rate,
233
+ sample_rand=sample_rand,
234
+ )
235
+ else:
236
+ logger.debug(
237
+ f"[Tracing.Sampler] Ignoring sampled param for non-root span {name}"
238
+ )
239
+
240
+ # Check if there is a traces_sampler
241
+ # Traces_sampler is responsible to check parent sampled to have full transactions.
242
+ has_traces_sampler = callable(client.options.get("traces_sampler"))
243
+
244
+ sample_rate_to_propagate = None
245
+
246
+ if is_root_span and has_traces_sampler:
247
+ sampling_context = create_sampling_context(
248
+ name, attributes, parent_span_context, trace_id
249
+ )
250
+ sample_rate = client.options["traces_sampler"](sampling_context)
251
+ sample_rate_to_propagate = sample_rate
252
+ else:
253
+ # Check if there is a parent with a sampling decision
254
+ if parent_sampled is not None:
255
+ sample_rate = bool(parent_sampled)
256
+ sample_rate_to_propagate = (
257
+ parent_sample_rate if parent_sample_rate else sample_rate
258
+ )
259
+ else:
260
+ # Check if there is a traces_sample_rate
261
+ sample_rate = client.options.get("traces_sample_rate")
262
+ sample_rate_to_propagate = sample_rate
263
+
264
+ # If the sample rate is invalid, drop the span
265
+ if not is_valid_sample_rate(sample_rate, source=self.__class__.__name__):
266
+ logger.warning(
267
+ f"[Tracing.Sampler] Discarding {name} because of invalid sample rate."
268
+ )
269
+ return dropped_result(parent_span_context, attributes)
270
+
271
+ # Down-sample in case of back pressure monitor says so
272
+ if is_root_span and client.monitor:
273
+ sample_rate /= 2**client.monitor.downsample_factor
274
+ if client.monitor.downsample_factor > 0:
275
+ sample_rate_to_propagate = sample_rate
276
+
277
+ # Compare sample_rand to sample_rate to make the final sampling decision
278
+ sample_rate = float(cast("Union[bool, float, int]", sample_rate))
279
+ sampled = sample_rand < Decimal.from_float(sample_rate)
280
+
281
+ if sampled:
282
+ if is_root_span:
283
+ logger.debug(
284
+ f"[Tracing.Sampler] Sampled #{name} with sample_rate: {sample_rate} and sample_rand: {sample_rand}"
285
+ )
286
+
287
+ return sampled_result(
288
+ parent_span_context,
289
+ attributes,
290
+ sample_rate=sample_rate_to_propagate,
291
+ sample_rand=None if sample_rand == parent_sample_rand else sample_rand,
292
+ )
293
+ else:
294
+ if is_root_span:
295
+ logger.debug(
296
+ f"[Tracing.Sampler] Dropped #{name} with sample_rate: {sample_rate} and sample_rand: {sample_rand}"
297
+ )
298
+
299
+ return dropped_result(
300
+ parent_span_context,
301
+ attributes,
302
+ sample_rate=sample_rate_to_propagate,
303
+ sample_rand=None if sample_rand == parent_sample_rand else sample_rand,
304
+ )
305
+
306
+ def get_description(self) -> str:
307
+ return self.__class__.__name__
308
+
309
+
310
+ def create_sampling_context(name, attributes, parent_span_context, trace_id):
311
+ # type: (str, Attributes, Optional[SpanContext], int) -> dict[str, Any]
312
+ sampling_context = {
313
+ "transaction_context": {
314
+ "name": name,
315
+ "op": attributes.get(SentrySpanAttribute.OP) if attributes else None,
316
+ "source": (
317
+ attributes.get(SentrySpanAttribute.SOURCE) if attributes else None
318
+ ),
319
+ },
320
+ "parent_sampled": get_parent_sampled(parent_span_context, trace_id),
321
+ } # type: dict[str, Any]
322
+
323
+ if attributes is not None:
324
+ sampling_context.update(attributes)
325
+
326
+ return sampling_context
@@ -0,0 +1,218 @@
1
+ from typing import cast
2
+ from contextlib import contextmanager
3
+ import warnings
4
+
5
+ from opentelemetry.context import (
6
+ get_value,
7
+ set_value,
8
+ attach,
9
+ detach,
10
+ get_current,
11
+ )
12
+ from opentelemetry.trace import (
13
+ SpanContext,
14
+ NonRecordingSpan,
15
+ TraceFlags,
16
+ TraceState,
17
+ use_span,
18
+ )
19
+
20
+ from sentry_sdk.opentelemetry.consts import (
21
+ SENTRY_SCOPES_KEY,
22
+ SENTRY_FORK_ISOLATION_SCOPE_KEY,
23
+ SENTRY_USE_CURRENT_SCOPE_KEY,
24
+ SENTRY_USE_ISOLATION_SCOPE_KEY,
25
+ TRACESTATE_SAMPLED_KEY,
26
+ )
27
+ from sentry_sdk.opentelemetry.contextvars_context import (
28
+ SentryContextVarsRuntimeContext,
29
+ )
30
+ from sentry_sdk.opentelemetry.utils import trace_state_from_baggage
31
+ from sentry_sdk.scope import Scope, ScopeType
32
+ from sentry_sdk.tracing import Span
33
+ from sentry_sdk._types import TYPE_CHECKING
34
+
35
+ if TYPE_CHECKING:
36
+ from typing import Tuple, Optional, Generator, Dict, Any
37
+
38
+
39
+ class PotelScope(Scope):
40
+ @classmethod
41
+ def _get_scopes(cls):
42
+ # type: () -> Optional[Tuple[PotelScope, PotelScope]]
43
+ """
44
+ Returns the current scopes tuple on the otel context. Internal use only.
45
+ """
46
+ return cast(
47
+ "Optional[Tuple[PotelScope, PotelScope]]", get_value(SENTRY_SCOPES_KEY)
48
+ )
49
+
50
+ @classmethod
51
+ def get_current_scope(cls):
52
+ # type: () -> PotelScope
53
+ """
54
+ Returns the current scope.
55
+ """
56
+ return cls._get_current_scope() or _INITIAL_CURRENT_SCOPE
57
+
58
+ @classmethod
59
+ def _get_current_scope(cls):
60
+ # type: () -> Optional[PotelScope]
61
+ """
62
+ Returns the current scope without creating a new one. Internal use only.
63
+ """
64
+ scopes = cls._get_scopes()
65
+ return scopes[0] if scopes else None
66
+
67
+ @classmethod
68
+ def get_isolation_scope(cls):
69
+ # type: () -> PotelScope
70
+ """
71
+ Returns the isolation scope.
72
+ """
73
+ return cls._get_isolation_scope() or _INITIAL_ISOLATION_SCOPE
74
+
75
+ @classmethod
76
+ def _get_isolation_scope(cls):
77
+ # type: () -> Optional[PotelScope]
78
+ """
79
+ Returns the isolation scope without creating a new one. Internal use only.
80
+ """
81
+ scopes = cls._get_scopes()
82
+ return scopes[1] if scopes else None
83
+
84
+ @contextmanager
85
+ def continue_trace(self, environ_or_headers):
86
+ # type: (Dict[str, Any]) -> Generator[None, None, None]
87
+ """
88
+ Sets the propagation context from environment or headers to continue an incoming trace.
89
+ Any span started within this context manager will use the same trace_id, parent_span_id
90
+ and inherit the sampling decision from the incoming trace.
91
+ """
92
+ self.generate_propagation_context(environ_or_headers)
93
+
94
+ span_context = self._incoming_otel_span_context()
95
+ if span_context is None:
96
+ yield
97
+ else:
98
+ with use_span(NonRecordingSpan(span_context)):
99
+ yield
100
+
101
+ def _incoming_otel_span_context(self):
102
+ # type: () -> Optional[SpanContext]
103
+ if self._propagation_context is None:
104
+ return None
105
+ # If sentry-trace extraction didn't have a parent_span_id, we don't have an upstream header
106
+ if self._propagation_context.parent_span_id is None:
107
+ return None
108
+
109
+ trace_flags = TraceFlags(
110
+ TraceFlags.SAMPLED
111
+ if self._propagation_context.parent_sampled
112
+ else TraceFlags.DEFAULT
113
+ )
114
+
115
+ if self._propagation_context.baggage:
116
+ trace_state = trace_state_from_baggage(self._propagation_context.baggage)
117
+ else:
118
+ trace_state = TraceState()
119
+
120
+ # for twp to work, we also need to consider deferred sampling when the sampling
121
+ # flag is not present, so the above TraceFlags are not sufficient
122
+ if self._propagation_context.parent_sampled is None:
123
+ trace_state = trace_state.update(TRACESTATE_SAMPLED_KEY, "deferred")
124
+
125
+ span_context = SpanContext(
126
+ trace_id=int(self._propagation_context.trace_id, 16),
127
+ span_id=int(self._propagation_context.parent_span_id, 16),
128
+ is_remote=True,
129
+ trace_flags=trace_flags,
130
+ trace_state=trace_state,
131
+ )
132
+
133
+ return span_context
134
+
135
+ def start_transaction(self, **kwargs):
136
+ # type: (Any) -> Span
137
+ """
138
+ .. deprecated:: 3.0.0
139
+ This function is deprecated and will be removed in a future release.
140
+ Use :py:meth:`sentry_sdk.start_span` instead.
141
+ """
142
+ warnings.warn(
143
+ "The `start_transaction` method is deprecated, please use `sentry_sdk.start_span instead.`",
144
+ DeprecationWarning,
145
+ stacklevel=2,
146
+ )
147
+ return self.start_span(**kwargs)
148
+
149
+ def start_span(self, **kwargs):
150
+ # type: (Any) -> Span
151
+ return Span(**kwargs)
152
+
153
+
154
+ _INITIAL_CURRENT_SCOPE = PotelScope(ty=ScopeType.CURRENT)
155
+ _INITIAL_ISOLATION_SCOPE = PotelScope(ty=ScopeType.ISOLATION)
156
+
157
+
158
+ def setup_initial_scopes():
159
+ # type: () -> None
160
+ global _INITIAL_CURRENT_SCOPE, _INITIAL_ISOLATION_SCOPE
161
+ _INITIAL_CURRENT_SCOPE = PotelScope(ty=ScopeType.CURRENT)
162
+ _INITIAL_ISOLATION_SCOPE = PotelScope(ty=ScopeType.ISOLATION)
163
+
164
+ scopes = (_INITIAL_CURRENT_SCOPE, _INITIAL_ISOLATION_SCOPE)
165
+ attach(set_value(SENTRY_SCOPES_KEY, scopes))
166
+
167
+
168
+ def setup_scope_context_management():
169
+ # type: () -> None
170
+ import opentelemetry.context
171
+
172
+ opentelemetry.context._RUNTIME_CONTEXT = SentryContextVarsRuntimeContext()
173
+ setup_initial_scopes()
174
+
175
+
176
+ @contextmanager
177
+ def isolation_scope():
178
+ # type: () -> Generator[PotelScope, None, None]
179
+ context = set_value(SENTRY_FORK_ISOLATION_SCOPE_KEY, True)
180
+ token = attach(context)
181
+ try:
182
+ yield PotelScope.get_isolation_scope()
183
+ finally:
184
+ detach(token)
185
+
186
+
187
+ @contextmanager
188
+ def new_scope():
189
+ # type: () -> Generator[PotelScope, None, None]
190
+ token = attach(get_current())
191
+ try:
192
+ yield PotelScope.get_current_scope()
193
+ finally:
194
+ detach(token)
195
+
196
+
197
+ @contextmanager
198
+ def use_scope(scope):
199
+ # type: (PotelScope) -> Generator[PotelScope, None, None]
200
+ context = set_value(SENTRY_USE_CURRENT_SCOPE_KEY, scope)
201
+ token = attach(context)
202
+
203
+ try:
204
+ yield scope
205
+ finally:
206
+ detach(token)
207
+
208
+
209
+ @contextmanager
210
+ def use_isolation_scope(isolation_scope):
211
+ # type: (PotelScope) -> Generator[PotelScope, None, None]
212
+ context = set_value(SENTRY_USE_ISOLATION_SCOPE_KEY, isolation_scope)
213
+ token = attach(context)
214
+
215
+ try:
216
+ yield isolation_scope
217
+ finally:
218
+ detach(token)