sentry-sdk 3.0.0a2__py2.py3-none-any.whl → 3.0.0a4__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 (159) hide show
  1. sentry_sdk/__init__.py +4 -0
  2. sentry_sdk/_compat.py +5 -12
  3. sentry_sdk/_init_implementation.py +7 -7
  4. sentry_sdk/_log_batcher.py +17 -29
  5. sentry_sdk/_lru_cache.py +7 -9
  6. sentry_sdk/_queue.py +2 -4
  7. sentry_sdk/_types.py +9 -16
  8. sentry_sdk/_werkzeug.py +5 -7
  9. sentry_sdk/ai/monitoring.py +45 -33
  10. sentry_sdk/ai/utils.py +8 -5
  11. sentry_sdk/api.py +91 -87
  12. sentry_sdk/attachments.py +10 -12
  13. sentry_sdk/client.py +119 -159
  14. sentry_sdk/consts.py +432 -223
  15. sentry_sdk/crons/api.py +16 -17
  16. sentry_sdk/crons/decorator.py +25 -27
  17. sentry_sdk/debug.py +4 -6
  18. sentry_sdk/envelope.py +46 -112
  19. sentry_sdk/feature_flags.py +9 -15
  20. sentry_sdk/integrations/__init__.py +24 -19
  21. sentry_sdk/integrations/_asgi_common.py +16 -18
  22. sentry_sdk/integrations/_wsgi_common.py +22 -33
  23. sentry_sdk/integrations/aiohttp.py +33 -31
  24. sentry_sdk/integrations/anthropic.py +43 -38
  25. sentry_sdk/integrations/argv.py +3 -4
  26. sentry_sdk/integrations/ariadne.py +16 -18
  27. sentry_sdk/integrations/arq.py +20 -29
  28. sentry_sdk/integrations/asgi.py +63 -37
  29. sentry_sdk/integrations/asyncio.py +15 -17
  30. sentry_sdk/integrations/asyncpg.py +1 -1
  31. sentry_sdk/integrations/atexit.py +6 -10
  32. sentry_sdk/integrations/aws_lambda.py +26 -36
  33. sentry_sdk/integrations/beam.py +10 -18
  34. sentry_sdk/integrations/boto3.py +20 -18
  35. sentry_sdk/integrations/bottle.py +25 -34
  36. sentry_sdk/integrations/celery/__init__.py +40 -59
  37. sentry_sdk/integrations/celery/beat.py +22 -26
  38. sentry_sdk/integrations/celery/utils.py +15 -17
  39. sentry_sdk/integrations/chalice.py +8 -10
  40. sentry_sdk/integrations/clickhouse_driver.py +22 -32
  41. sentry_sdk/integrations/cloud_resource_context.py +9 -16
  42. sentry_sdk/integrations/cohere.py +19 -25
  43. sentry_sdk/integrations/dedupe.py +5 -8
  44. sentry_sdk/integrations/django/__init__.py +69 -74
  45. sentry_sdk/integrations/django/asgi.py +25 -33
  46. sentry_sdk/integrations/django/caching.py +24 -20
  47. sentry_sdk/integrations/django/middleware.py +18 -21
  48. sentry_sdk/integrations/django/signals_handlers.py +12 -11
  49. sentry_sdk/integrations/django/templates.py +21 -18
  50. sentry_sdk/integrations/django/transactions.py +16 -11
  51. sentry_sdk/integrations/django/views.py +8 -12
  52. sentry_sdk/integrations/dramatiq.py +21 -21
  53. sentry_sdk/integrations/excepthook.py +10 -10
  54. sentry_sdk/integrations/executing.py +3 -4
  55. sentry_sdk/integrations/falcon.py +27 -42
  56. sentry_sdk/integrations/fastapi.py +13 -16
  57. sentry_sdk/integrations/flask.py +31 -38
  58. sentry_sdk/integrations/gcp.py +13 -16
  59. sentry_sdk/integrations/gnu_backtrace.py +7 -20
  60. sentry_sdk/integrations/gql.py +16 -17
  61. sentry_sdk/integrations/graphene.py +14 -13
  62. sentry_sdk/integrations/grpc/__init__.py +3 -2
  63. sentry_sdk/integrations/grpc/aio/client.py +2 -2
  64. sentry_sdk/integrations/grpc/aio/server.py +15 -14
  65. sentry_sdk/integrations/grpc/client.py +21 -11
  66. sentry_sdk/integrations/grpc/consts.py +2 -0
  67. sentry_sdk/integrations/grpc/server.py +12 -8
  68. sentry_sdk/integrations/httpx.py +11 -14
  69. sentry_sdk/integrations/huey.py +14 -21
  70. sentry_sdk/integrations/huggingface_hub.py +17 -17
  71. sentry_sdk/integrations/langchain.py +204 -114
  72. sentry_sdk/integrations/launchdarkly.py +13 -10
  73. sentry_sdk/integrations/litestar.py +40 -38
  74. sentry_sdk/integrations/logging.py +29 -36
  75. sentry_sdk/integrations/loguru.py +16 -20
  76. sentry_sdk/integrations/modules.py +3 -4
  77. sentry_sdk/integrations/openai.py +421 -204
  78. sentry_sdk/integrations/openai_agents/__init__.py +49 -0
  79. sentry_sdk/integrations/openai_agents/consts.py +1 -0
  80. sentry_sdk/integrations/openai_agents/patches/__init__.py +4 -0
  81. sentry_sdk/integrations/openai_agents/patches/agent_run.py +152 -0
  82. sentry_sdk/integrations/openai_agents/patches/models.py +52 -0
  83. sentry_sdk/integrations/openai_agents/patches/runner.py +42 -0
  84. sentry_sdk/integrations/openai_agents/patches/tools.py +84 -0
  85. sentry_sdk/integrations/openai_agents/spans/__init__.py +5 -0
  86. sentry_sdk/integrations/openai_agents/spans/agent_workflow.py +20 -0
  87. sentry_sdk/integrations/openai_agents/spans/ai_client.py +46 -0
  88. sentry_sdk/integrations/openai_agents/spans/execute_tool.py +47 -0
  89. sentry_sdk/integrations/openai_agents/spans/handoff.py +24 -0
  90. sentry_sdk/integrations/openai_agents/spans/invoke_agent.py +41 -0
  91. sentry_sdk/integrations/openai_agents/utils.py +153 -0
  92. sentry_sdk/integrations/openfeature.py +12 -8
  93. sentry_sdk/integrations/pure_eval.py +6 -10
  94. sentry_sdk/integrations/pymongo.py +14 -18
  95. sentry_sdk/integrations/pyramid.py +31 -36
  96. sentry_sdk/integrations/quart.py +23 -28
  97. sentry_sdk/integrations/ray.py +73 -64
  98. sentry_sdk/integrations/redis/__init__.py +7 -4
  99. sentry_sdk/integrations/redis/_async_common.py +18 -12
  100. sentry_sdk/integrations/redis/_sync_common.py +16 -15
  101. sentry_sdk/integrations/redis/modules/caches.py +17 -8
  102. sentry_sdk/integrations/redis/modules/queries.py +9 -8
  103. sentry_sdk/integrations/redis/rb.py +3 -2
  104. sentry_sdk/integrations/redis/redis.py +4 -4
  105. sentry_sdk/integrations/redis/redis_cluster.py +10 -8
  106. sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +3 -2
  107. sentry_sdk/integrations/redis/utils.py +21 -22
  108. sentry_sdk/integrations/rq.py +13 -16
  109. sentry_sdk/integrations/rust_tracing.py +10 -7
  110. sentry_sdk/integrations/sanic.py +34 -46
  111. sentry_sdk/integrations/serverless.py +22 -27
  112. sentry_sdk/integrations/socket.py +29 -17
  113. sentry_sdk/integrations/spark/__init__.py +1 -0
  114. sentry_sdk/integrations/spark/spark_driver.py +45 -83
  115. sentry_sdk/integrations/spark/spark_worker.py +7 -11
  116. sentry_sdk/integrations/sqlalchemy.py +22 -19
  117. sentry_sdk/integrations/starlette.py +89 -93
  118. sentry_sdk/integrations/starlite.py +31 -37
  119. sentry_sdk/integrations/statsig.py +5 -4
  120. sentry_sdk/integrations/stdlib.py +32 -28
  121. sentry_sdk/integrations/strawberry.py +63 -50
  122. sentry_sdk/integrations/sys_exit.py +7 -11
  123. sentry_sdk/integrations/threading.py +13 -15
  124. sentry_sdk/integrations/tornado.py +28 -32
  125. sentry_sdk/integrations/trytond.py +4 -3
  126. sentry_sdk/integrations/typer.py +8 -6
  127. sentry_sdk/integrations/unleash.py +5 -4
  128. sentry_sdk/integrations/wsgi.py +47 -46
  129. sentry_sdk/logger.py +13 -9
  130. sentry_sdk/monitor.py +16 -28
  131. sentry_sdk/opentelemetry/consts.py +11 -4
  132. sentry_sdk/opentelemetry/contextvars_context.py +17 -15
  133. sentry_sdk/opentelemetry/propagator.py +38 -21
  134. sentry_sdk/opentelemetry/sampler.py +51 -34
  135. sentry_sdk/opentelemetry/scope.py +46 -37
  136. sentry_sdk/opentelemetry/span_processor.py +43 -59
  137. sentry_sdk/opentelemetry/tracing.py +32 -12
  138. sentry_sdk/opentelemetry/utils.py +180 -196
  139. sentry_sdk/profiler/continuous_profiler.py +108 -97
  140. sentry_sdk/profiler/transaction_profiler.py +70 -97
  141. sentry_sdk/profiler/utils.py +11 -15
  142. sentry_sdk/scope.py +251 -264
  143. sentry_sdk/scrubber.py +22 -26
  144. sentry_sdk/serializer.py +48 -65
  145. sentry_sdk/session.py +44 -61
  146. sentry_sdk/sessions.py +35 -49
  147. sentry_sdk/spotlight.py +15 -21
  148. sentry_sdk/tracing.py +118 -184
  149. sentry_sdk/tracing_utils.py +103 -123
  150. sentry_sdk/transport.py +131 -157
  151. sentry_sdk/utils.py +278 -309
  152. sentry_sdk/worker.py +16 -28
  153. {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a4.dist-info}/METADATA +1 -1
  154. sentry_sdk-3.0.0a4.dist-info/RECORD +168 -0
  155. sentry_sdk-3.0.0a2.dist-info/RECORD +0 -154
  156. {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a4.dist-info}/WHEEL +0 -0
  157. {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a4.dist-info}/entry_points.txt +0 -0
  158. {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a4.dist-info}/licenses/LICENSE +0 -0
  159. {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a4.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,4 @@
1
+ from __future__ import annotations
1
2
  import sentry_sdk
2
3
  from sentry_sdk.utils import (
3
4
  capture_internal_exceptions,
@@ -30,15 +31,16 @@ class TyperIntegration(Integration):
30
31
  identifier = "typer"
31
32
 
32
33
  @staticmethod
33
- def setup_once():
34
- # type: () -> None
34
+ def setup_once() -> None:
35
35
  typer.main.except_hook = _make_excepthook(typer.main.except_hook) # type: ignore
36
36
 
37
37
 
38
- def _make_excepthook(old_excepthook):
39
- # type: (Excepthook) -> Excepthook
40
- def sentry_sdk_excepthook(type_, value, traceback):
41
- # type: (Type[BaseException], BaseException, Optional[TracebackType]) -> None
38
+ def _make_excepthook(old_excepthook: Excepthook) -> Excepthook:
39
+ def sentry_sdk_excepthook(
40
+ type_: Type[BaseException],
41
+ value: BaseException,
42
+ traceback: Optional[TracebackType],
43
+ ) -> None:
42
44
  integration = sentry_sdk.get_client().get_integration(TyperIntegration)
43
45
 
44
46
  # Note: If we replace this with ensure_integration_enabled then
@@ -1,3 +1,4 @@
1
+ from __future__ import annotations
1
2
  from functools import wraps
2
3
  from typing import Any
3
4
 
@@ -14,14 +15,14 @@ class UnleashIntegration(Integration):
14
15
  identifier = "unleash"
15
16
 
16
17
  @staticmethod
17
- def setup_once():
18
- # type: () -> None
18
+ def setup_once() -> None:
19
19
  # Wrap and patch evaluation methods (class methods)
20
20
  old_is_enabled = UnleashClient.is_enabled
21
21
 
22
22
  @wraps(old_is_enabled)
23
- def sentry_is_enabled(self, feature, *args, **kwargs):
24
- # type: (UnleashClient, str, *Any, **Any) -> Any
23
+ def sentry_is_enabled(
24
+ self: UnleashClient, feature: str, *args: Any, **kwargs: Any
25
+ ) -> Any:
25
26
  enabled = old_is_enabled(self, feature, *args, **kwargs)
26
27
 
27
28
  # We have no way of knowing what type of unleash feature this is, so we have to treat
@@ -1,3 +1,4 @@
1
+ from __future__ import annotations
1
2
  import sys
2
3
  from functools import partial
3
4
 
@@ -25,22 +26,26 @@ if TYPE_CHECKING:
25
26
  from typing import Callable
26
27
  from typing import Dict
27
28
  from typing import Iterator
29
+ from typing import Iterable
28
30
  from typing import Any
29
31
  from typing import Tuple
32
+ from typing import List
30
33
  from typing import Optional
31
- from typing import TypeVar
32
34
  from typing import Protocol
33
35
 
34
36
  from sentry_sdk.utils import ExcInfo
35
37
  from sentry_sdk._types import Event, EventProcessor
36
38
 
37
- WsgiResponseIter = TypeVar("WsgiResponseIter")
38
- WsgiResponseHeaders = TypeVar("WsgiResponseHeaders")
39
- WsgiExcInfo = TypeVar("WsgiExcInfo")
39
+ WsgiResponseIter = Iterable[bytes]
40
+ WsgiResponseHeaders = List[Tuple[str, str]]
40
41
 
41
42
  class StartResponse(Protocol):
42
- def __call__(self, status, response_headers, exc_info=None): # type: ignore
43
- # type: (str, WsgiResponseHeaders, Optional[WsgiExcInfo]) -> WsgiResponseIter
43
+ def __call__(
44
+ self,
45
+ status: str,
46
+ response_headers: WsgiResponseHeaders,
47
+ exc_info: Optional[ExcInfo] = None,
48
+ ) -> WsgiResponseIter:
44
49
  pass
45
50
 
46
51
 
@@ -58,13 +63,11 @@ ENVIRON_TO_ATTRIBUTE = {
58
63
  }
59
64
 
60
65
 
61
- def wsgi_decoding_dance(s, charset="utf-8", errors="replace"):
62
- # type: (str, str, str) -> str
66
+ def wsgi_decoding_dance(s: str, charset: str = "utf-8", errors: str = "replace") -> str:
63
67
  return s.encode("latin1").decode(charset, errors)
64
68
 
65
69
 
66
- def get_request_url(environ, use_x_forwarded_for=False):
67
- # type: (Dict[str, str], bool) -> str
70
+ def get_request_url(environ: Dict[str, str], use_x_forwarded_for: bool = False) -> str:
68
71
  """Return the absolute URL without query string for the given WSGI
69
72
  environment."""
70
73
  script_name = environ.get("SCRIPT_NAME", "").rstrip("/")
@@ -88,19 +91,19 @@ class SentryWsgiMiddleware:
88
91
 
89
92
  def __init__(
90
93
  self,
91
- app, # type: Callable[[Dict[str, str], Callable[..., Any]], Any]
92
- use_x_forwarded_for=False, # type: bool
93
- span_origin=None, # type: Optional[str]
94
- http_methods_to_capture=DEFAULT_HTTP_METHODS_TO_CAPTURE, # type: Tuple[str, ...]
95
- ):
96
- # type: (...) -> None
94
+ app: Callable[[Dict[str, str], Callable[..., Any]], Any],
95
+ use_x_forwarded_for: bool = False,
96
+ span_origin: Optional[str] = None,
97
+ http_methods_to_capture: Tuple[str, ...] = DEFAULT_HTTP_METHODS_TO_CAPTURE,
98
+ ) -> None:
97
99
  self.app = app
98
100
  self.use_x_forwarded_for = use_x_forwarded_for
99
101
  self.span_origin = span_origin
100
102
  self.http_methods_to_capture = http_methods_to_capture
101
103
 
102
- def __call__(self, environ, start_response):
103
- # type: (Dict[str, str], Callable[..., Any]) -> _ScopedResponse
104
+ def __call__(
105
+ self, environ: Dict[str, str], start_response: Callable[..., Any]
106
+ ) -> _ScopedResponse:
104
107
  if _wsgi_middleware_applied.get(False):
105
108
  return self.app(environ, start_response)
106
109
 
@@ -144,8 +147,12 @@ class SentryWsgiMiddleware:
144
147
 
145
148
  return _ScopedResponse(scope, response)
146
149
 
147
- def _run_original_app(self, environ, start_response, span):
148
- # type: (dict[str, str], StartResponse, Optional[Span]) -> Any
150
+ def _run_original_app(
151
+ self,
152
+ environ: dict[str, str],
153
+ start_response: StartResponse,
154
+ span: Optional[Span],
155
+ ) -> Any:
149
156
  try:
150
157
  return self.app(
151
158
  environ,
@@ -159,14 +166,13 @@ class SentryWsgiMiddleware:
159
166
  reraise(*_capture_exception())
160
167
 
161
168
 
162
- def _sentry_start_response( # type: ignore
163
- old_start_response, # type: StartResponse
164
- span, # type: Optional[Span]
165
- status, # type: str
166
- response_headers, # type: WsgiResponseHeaders
167
- exc_info=None, # type: Optional[WsgiExcInfo]
168
- ):
169
- # type: (...) -> WsgiResponseIter
169
+ def _sentry_start_response(
170
+ old_start_response: StartResponse,
171
+ span: Optional[Span],
172
+ status: str,
173
+ response_headers: WsgiResponseHeaders,
174
+ exc_info: Optional[ExcInfo] = None,
175
+ ) -> WsgiResponseIter:
170
176
  with capture_internal_exceptions():
171
177
  status_int = int(status.split(" ", 1)[0])
172
178
  if span is not None:
@@ -181,8 +187,7 @@ def _sentry_start_response( # type: ignore
181
187
  return old_start_response(status, response_headers, exc_info)
182
188
 
183
189
 
184
- def _get_environ(environ):
185
- # type: (Dict[str, str]) -> Iterator[Tuple[str, str]]
190
+ def _get_environ(environ: Dict[str, str]) -> Iterator[Tuple[str, str]]:
186
191
  """
187
192
  Returns our explicitly included environment variables we want to
188
193
  capture (server name, port and remote addr if pii is enabled).
@@ -198,8 +203,7 @@ def _get_environ(environ):
198
203
  yield key, environ[key]
199
204
 
200
205
 
201
- def get_client_ip(environ):
202
- # type: (Dict[str, str]) -> Optional[Any]
206
+ def get_client_ip(environ: Dict[str, str]) -> Optional[Any]:
203
207
  """
204
208
  Infer the user IP address from various headers. This cannot be used in
205
209
  security sensitive situations since the value may be forged from a client,
@@ -218,8 +222,7 @@ def get_client_ip(environ):
218
222
  return environ.get("REMOTE_ADDR")
219
223
 
220
224
 
221
- def _capture_exception():
222
- # type: () -> ExcInfo
225
+ def _capture_exception() -> ExcInfo:
223
226
  """
224
227
  Captures the current exception and sends it to Sentry.
225
228
  Returns the ExcInfo tuple to it can be reraised afterwards.
@@ -253,13 +256,11 @@ class _ScopedResponse:
253
256
 
254
257
  __slots__ = ("_response", "_scope")
255
258
 
256
- def __init__(self, scope, response):
257
- # type: (sentry_sdk.Scope, Iterator[bytes]) -> None
259
+ def __init__(self, scope: sentry_sdk.Scope, response: Iterator[bytes]) -> None:
258
260
  self._scope = scope
259
261
  self._response = response
260
262
 
261
- def __iter__(self):
262
- # type: () -> Iterator[bytes]
263
+ def __iter__(self) -> Iterator[bytes]:
263
264
  iterator = iter(self._response)
264
265
 
265
266
  while True:
@@ -273,8 +274,7 @@ class _ScopedResponse:
273
274
 
274
275
  yield chunk
275
276
 
276
- def close(self):
277
- # type: () -> None
277
+ def close(self) -> None:
278
278
  with sentry_sdk.use_isolation_scope(self._scope):
279
279
  try:
280
280
  self._response.close() # type: ignore
@@ -284,8 +284,9 @@ class _ScopedResponse:
284
284
  reraise(*_capture_exception())
285
285
 
286
286
 
287
- def _make_wsgi_event_processor(environ, use_x_forwarded_for):
288
- # type: (Dict[str, str], bool) -> EventProcessor
287
+ def _make_wsgi_event_processor(
288
+ environ: Dict[str, str], use_x_forwarded_for: bool
289
+ ) -> EventProcessor:
289
290
  # It's a bit unfortunate that we have to extract and parse the request data
290
291
  # from the environ so eagerly, but there are a few good reasons for this.
291
292
  #
@@ -305,8 +306,7 @@ def _make_wsgi_event_processor(environ, use_x_forwarded_for):
305
306
  env = dict(_get_environ(environ))
306
307
  headers = _filter_headers(dict(_get_headers(environ)))
307
308
 
308
- def event_processor(event, hint):
309
- # type: (Event, Dict[str, Any]) -> Event
309
+ def event_processor(event: Event, hint: Dict[str, Any]) -> Event:
310
310
  with capture_internal_exceptions():
311
311
  # if the code below fails halfway through we at least have some data
312
312
  request_info = event.setdefault("request", {})
@@ -327,8 +327,9 @@ def _make_wsgi_event_processor(environ, use_x_forwarded_for):
327
327
  return event_processor
328
328
 
329
329
 
330
- def _prepopulate_attributes(wsgi_environ, use_x_forwarded_for=False):
331
- # type: (dict[str, str], bool) -> dict[str, str]
330
+ def _prepopulate_attributes(
331
+ wsgi_environ: dict[str, str], use_x_forwarded_for: bool = False
332
+ ) -> dict[str, str]:
332
333
  """Extract span attributes from the WSGI environment."""
333
334
  attributes = {}
334
335
 
sentry_sdk/logger.py CHANGED
@@ -1,7 +1,12 @@
1
1
  # NOTE: this is the logger sentry exposes to users, not some generic logger.
2
+ from __future__ import annotations
2
3
  import functools
3
4
  import time
4
- from typing import Any
5
+
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from typing import Any
5
10
 
6
11
  from sentry_sdk import get_client
7
12
  from sentry_sdk.utils import safe_repr
@@ -18,13 +23,14 @@ OTEL_RANGES = [
18
23
  ]
19
24
 
20
25
 
21
- def _capture_log(severity_text, severity_number, template, **kwargs):
22
- # type: (str, int, str, **Any) -> None
26
+ def _capture_log(
27
+ severity_text: str, severity_number: int, template: str, **kwargs: Any
28
+ ) -> None:
23
29
  client = get_client()
24
30
 
25
- attrs = {
31
+ attrs: dict[str, str | bool | float | int] = {
26
32
  "sentry.message.template": template,
27
- } # type: dict[str, str | bool | float | int]
33
+ }
28
34
  if "attributes" in kwargs:
29
35
  attrs.update(kwargs.pop("attributes"))
30
36
  for k, v in kwargs.items():
@@ -65,8 +71,7 @@ error = functools.partial(_capture_log, "error", 17)
65
71
  fatal = functools.partial(_capture_log, "fatal", 21)
66
72
 
67
73
 
68
- def _otel_severity_text(otel_severity_number):
69
- # type: (int) -> str
74
+ def _otel_severity_text(otel_severity_number: int) -> str:
70
75
  for (lower, upper), severity in OTEL_RANGES:
71
76
  if lower <= otel_severity_number <= upper:
72
77
  return severity
@@ -74,8 +79,7 @@ def _otel_severity_text(otel_severity_number):
74
79
  return "default"
75
80
 
76
81
 
77
- def _log_level_to_otel(level, mapping):
78
- # type: (int, dict[Any, int]) -> tuple[int, str]
82
+ def _log_level_to_otel(level: int, mapping: dict[Any, int]) -> tuple[int, str]:
79
83
  for py_level, otel_severity_number in sorted(mapping.items(), reverse=True):
80
84
  if level >= py_level:
81
85
  return otel_severity_number, _otel_severity_text(otel_severity_number)
sentry_sdk/monitor.py CHANGED
@@ -1,14 +1,15 @@
1
+ from __future__ import annotations
1
2
  import os
2
3
  import time
3
4
  from threading import Thread, Lock
4
5
 
5
- import sentry_sdk
6
6
  from sentry_sdk.utils import logger
7
7
 
8
8
  from typing import TYPE_CHECKING
9
9
 
10
10
  if TYPE_CHECKING:
11
11
  from typing import Optional
12
+ from sentry_sdk.transport import Transport
12
13
 
13
14
 
14
15
  MAX_DOWNSAMPLE_FACTOR = 10
@@ -23,21 +24,19 @@ class Monitor:
23
24
 
24
25
  name = "sentry.monitor"
25
26
 
26
- def __init__(self, transport, interval=10):
27
- # type: (sentry_sdk.transport.Transport, float) -> None
28
- self.transport = transport # type: sentry_sdk.transport.Transport
29
- self.interval = interval # type: float
27
+ def __init__(self, transport: Transport, interval: float = 10) -> None:
28
+ self.transport: Transport = transport
29
+ self.interval: float = interval
30
30
 
31
31
  self._healthy = True
32
- self._downsample_factor = 0 # type: int
32
+ self._downsample_factor: int = 0
33
33
 
34
- self._thread = None # type: Optional[Thread]
34
+ self._thread: Optional[Thread] = None
35
35
  self._thread_lock = Lock()
36
- self._thread_for_pid = None # type: Optional[int]
36
+ self._thread_for_pid: Optional[int] = None
37
37
  self._running = True
38
38
 
39
- def _ensure_running(self):
40
- # type: () -> None
39
+ def _ensure_running(self) -> None:
41
40
  """
42
41
  Check that the monitor has an active thread to run in, or create one if not.
43
42
 
@@ -52,8 +51,7 @@ class Monitor:
52
51
  if self._thread_for_pid == os.getpid() and self._thread is not None:
53
52
  return None
54
53
 
55
- def _thread():
56
- # type: (...) -> None
54
+ def _thread() -> None:
57
55
  while self._running:
58
56
  time.sleep(self.interval)
59
57
  if self._running:
@@ -74,13 +72,11 @@ class Monitor:
74
72
 
75
73
  return None
76
74
 
77
- def run(self):
78
- # type: () -> None
75
+ def run(self) -> None:
79
76
  self.check_health()
80
77
  self.set_downsample_factor()
81
78
 
82
- def set_downsample_factor(self):
83
- # type: () -> None
79
+ def set_downsample_factor(self) -> None:
84
80
  if self._healthy:
85
81
  if self._downsample_factor > 0:
86
82
  logger.debug(
@@ -95,8 +91,7 @@ class Monitor:
95
91
  self._downsample_factor,
96
92
  )
97
93
 
98
- def check_health(self):
99
- # type: () -> None
94
+ def check_health(self) -> None:
100
95
  """
101
96
  Perform the actual health checks,
102
97
  currently only checks if the transport is rate-limited.
@@ -104,21 +99,14 @@ class Monitor:
104
99
  """
105
100
  self._healthy = self.transport.is_healthy()
106
101
 
107
- def is_healthy(self):
108
- # type: () -> bool
102
+ def is_healthy(self) -> bool:
109
103
  self._ensure_running()
110
104
  return self._healthy
111
105
 
112
106
  @property
113
- def downsample_factor(self):
114
- # type: () -> int
107
+ def downsample_factor(self) -> int:
115
108
  self._ensure_running()
116
109
  return self._downsample_factor
117
110
 
118
- def kill(self):
119
- # type: () -> None
111
+ def kill(self) -> None:
120
112
  self._running = False
121
-
122
- def __del__(self):
123
- # type: () -> None
124
- self.kill()
@@ -1,5 +1,4 @@
1
1
  from opentelemetry.context import create_key
2
- from sentry_sdk.tracing_utils import Baggage
3
2
 
4
3
 
5
4
  # propagation keys
@@ -13,14 +12,22 @@ SENTRY_USE_CURRENT_SCOPE_KEY = create_key("sentry_use_current_scope")
13
12
  SENTRY_USE_ISOLATION_SCOPE_KEY = create_key("sentry_use_isolation_scope")
14
13
 
15
14
  # trace state keys
16
- TRACESTATE_SAMPLED_KEY = Baggage.SENTRY_PREFIX + "sampled"
17
- TRACESTATE_SAMPLE_RATE_KEY = Baggage.SENTRY_PREFIX + "sample_rate"
18
- TRACESTATE_SAMPLE_RAND_KEY = Baggage.SENTRY_PREFIX + "sample_rand"
15
+ SENTRY_PREFIX = "sentry-"
16
+ TRACESTATE_SAMPLED_KEY = SENTRY_PREFIX + "sampled"
17
+ TRACESTATE_SAMPLE_RATE_KEY = SENTRY_PREFIX + "sample_rate"
18
+ TRACESTATE_SAMPLE_RAND_KEY = SENTRY_PREFIX + "sample_rand"
19
19
 
20
20
  # misc
21
21
  OTEL_SENTRY_CONTEXT = "otel"
22
22
  SPAN_ORIGIN = "auto.otel"
23
23
 
24
+ # resource semconv attributes
25
+ # Not all of these are stable yet, so defining them here rather than importing.
26
+ # https://github.com/open-telemetry/semantic-conventions/blob/main/docs/resource/README.md#service
27
+ RESOURCE_SERVICE_NAME = "service.name"
28
+ RESOURCE_SERVICE_NAMESPACE = "service.namespace"
29
+ RESOURCE_SERVICE_VERSION = "service.version"
30
+
24
31
 
25
32
  class SentrySpanAttribute:
26
33
  DESCRIPTION = "sentry.description"
@@ -1,4 +1,5 @@
1
- from typing import cast, TYPE_CHECKING
1
+ from __future__ import annotations
2
+ from typing import TYPE_CHECKING
2
3
 
3
4
  from opentelemetry.trace import get_current_span, set_span_in_context
4
5
  from opentelemetry.trace.span import INVALID_SPAN
@@ -13,36 +14,37 @@ from sentry_sdk.opentelemetry.consts import (
13
14
  SENTRY_USE_CURRENT_SCOPE_KEY,
14
15
  SENTRY_USE_ISOLATION_SCOPE_KEY,
15
16
  )
17
+ from sentry_sdk.opentelemetry.scope import PotelScope, validate_scopes
16
18
 
17
19
  if TYPE_CHECKING:
18
- from typing import Optional
19
20
  from contextvars import Token
20
- import sentry_sdk.opentelemetry.scope as scope
21
21
 
22
22
 
23
23
  class SentryContextVarsRuntimeContext(ContextVarsRuntimeContext):
24
- def attach(self, context):
25
- # type: (Context) -> Token[Context]
26
- scopes = get_value(SENTRY_SCOPES_KEY, context)
24
+ def attach(self, context: Context) -> Token[Context]:
25
+ scopes = validate_scopes(get_value(SENTRY_SCOPES_KEY, context))
27
26
 
28
- should_fork_isolation_scope = context.pop(
29
- SENTRY_FORK_ISOLATION_SCOPE_KEY, False
27
+ should_fork_isolation_scope = bool(
28
+ context.pop(SENTRY_FORK_ISOLATION_SCOPE_KEY, False)
30
29
  )
31
- should_fork_isolation_scope = cast("bool", should_fork_isolation_scope)
32
30
 
33
31
  should_use_isolation_scope = context.pop(SENTRY_USE_ISOLATION_SCOPE_KEY, None)
34
- should_use_isolation_scope = cast(
35
- "Optional[scope.PotelScope]", should_use_isolation_scope
32
+ should_use_isolation_scope = (
33
+ should_use_isolation_scope
34
+ if isinstance(should_use_isolation_scope, PotelScope)
35
+ else None
36
36
  )
37
37
 
38
38
  should_use_current_scope = context.pop(SENTRY_USE_CURRENT_SCOPE_KEY, None)
39
- should_use_current_scope = cast(
40
- "Optional[scope.PotelScope]", should_use_current_scope
39
+ should_use_current_scope = (
40
+ should_use_current_scope
41
+ if isinstance(should_use_current_scope, PotelScope)
42
+ else None
41
43
  )
42
44
 
43
45
  if scopes:
44
- scopes = cast("tuple[scope.PotelScope, scope.PotelScope]", scopes)
45
- (current_scope, isolation_scope) = scopes
46
+ current_scope = scopes[0]
47
+ isolation_scope = scopes[1]
46
48
  else:
47
49
  current_scope = sentry_sdk.get_current_scope()
48
50
  isolation_scope = sentry_sdk.get_isolation_scope()
@@ -1,4 +1,4 @@
1
- from typing import cast
1
+ from __future__ import annotations
2
2
 
3
3
  from opentelemetry import trace
4
4
  from opentelemetry.context import (
@@ -20,7 +20,9 @@ from opentelemetry.trace import (
20
20
  SpanContext,
21
21
  TraceFlags,
22
22
  )
23
+ from opentelemetry.semconv.trace import SpanAttributes
23
24
 
25
+ import sentry_sdk
24
26
  from sentry_sdk.consts import (
25
27
  BAGGAGE_HEADER_NAME,
26
28
  SENTRY_TRACE_HEADER_NAME,
@@ -30,13 +32,17 @@ from sentry_sdk.opentelemetry.consts import (
30
32
  SENTRY_TRACE_KEY,
31
33
  SENTRY_SCOPES_KEY,
32
34
  )
33
- from sentry_sdk.tracing_utils import Baggage, extract_sentrytrace_data
35
+ from sentry_sdk.tracing_utils import (
36
+ Baggage,
37
+ extract_sentrytrace_data,
38
+ should_propagate_trace,
39
+ )
40
+ from sentry_sdk.opentelemetry.scope import validate_scopes
34
41
 
35
42
  from typing import TYPE_CHECKING
36
43
 
37
44
  if TYPE_CHECKING:
38
45
  from typing import Optional, Set
39
- import sentry_sdk.opentelemetry.scope as scope
40
46
 
41
47
 
42
48
  class SentryPropagator(TextMapPropagator):
@@ -44,8 +50,12 @@ class SentryPropagator(TextMapPropagator):
44
50
  Propagates tracing headers for Sentry's tracing system in a way OTel understands.
45
51
  """
46
52
 
47
- def extract(self, carrier, context=None, getter=default_getter):
48
- # type: (CarrierT, Optional[Context], Getter[CarrierT]) -> Context
53
+ def extract(
54
+ self,
55
+ carrier: CarrierT,
56
+ context: Optional[Context] = None,
57
+ getter: Getter[CarrierT] = default_getter,
58
+ ) -> Context:
49
59
  if context is None:
50
60
  context = get_current()
51
61
 
@@ -87,22 +97,29 @@ class SentryPropagator(TextMapPropagator):
87
97
  modified_context = trace.set_span_in_context(span, context)
88
98
  return modified_context
89
99
 
90
- def inject(self, carrier, context=None, setter=default_setter):
91
- # type: (CarrierT, Optional[Context], Setter[CarrierT]) -> None
92
- if context is None:
93
- context = get_current()
94
-
95
- scopes = get_value(SENTRY_SCOPES_KEY, context)
96
- if scopes:
97
- scopes = cast("tuple[scope.PotelScope, scope.PotelScope]", scopes)
98
- (current_scope, _) = scopes
99
-
100
- # TODO-neel-potel check trace_propagation_targets
101
- # TODO-neel-potel test propagator works with twp
102
- for key, value in current_scope.iter_trace_propagation_headers():
103
- setter.set(carrier, key, value)
100
+ def inject(
101
+ self,
102
+ carrier: CarrierT,
103
+ context: Optional[Context] = None,
104
+ setter: Setter[CarrierT] = default_setter,
105
+ ) -> None:
106
+ scopes = validate_scopes(get_value(SENTRY_SCOPES_KEY, context))
107
+ if not scopes:
108
+ return
109
+
110
+ (current_scope, _) = scopes
111
+
112
+ span = current_scope.span
113
+ if span:
114
+ span_url = span.get_attribute(SpanAttributes.HTTP_URL)
115
+ if span_url and not should_propagate_trace(
116
+ sentry_sdk.get_client(), span_url
117
+ ):
118
+ return
119
+
120
+ for key, value in current_scope.iter_trace_propagation_headers():
121
+ setter.set(carrier, key, value)
104
122
 
105
123
  @property
106
- def fields(self):
107
- # type: () -> Set[str]
124
+ def fields(self) -> Set[str]:
108
125
  return {SENTRY_TRACE_HEADER_NAME, BAGGAGE_HEADER_NAME}