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.

Files changed (109) hide show
  1. sentry_sdk/__init__.py +3 -8
  2. sentry_sdk/_compat.py +0 -1
  3. sentry_sdk/_init_implementation.py +6 -44
  4. sentry_sdk/_types.py +2 -64
  5. sentry_sdk/ai/monitoring.py +14 -10
  6. sentry_sdk/ai/utils.py +1 -1
  7. sentry_sdk/api.py +56 -169
  8. sentry_sdk/client.py +27 -72
  9. sentry_sdk/consts.py +60 -23
  10. sentry_sdk/debug.py +0 -10
  11. sentry_sdk/envelope.py +1 -3
  12. sentry_sdk/feature_flags.py +1 -1
  13. sentry_sdk/integrations/__init__.py +4 -2
  14. sentry_sdk/integrations/_asgi_common.py +5 -6
  15. sentry_sdk/integrations/_wsgi_common.py +11 -40
  16. sentry_sdk/integrations/aiohttp.py +104 -57
  17. sentry_sdk/integrations/anthropic.py +10 -7
  18. sentry_sdk/integrations/arq.py +24 -13
  19. sentry_sdk/integrations/asgi.py +102 -83
  20. sentry_sdk/integrations/asyncio.py +1 -0
  21. sentry_sdk/integrations/asyncpg.py +45 -30
  22. sentry_sdk/integrations/aws_lambda.py +109 -92
  23. sentry_sdk/integrations/boto3.py +38 -9
  24. sentry_sdk/integrations/bottle.py +1 -1
  25. sentry_sdk/integrations/celery/__init__.py +51 -41
  26. sentry_sdk/integrations/clickhouse_driver.py +59 -28
  27. sentry_sdk/integrations/cohere.py +2 -0
  28. sentry_sdk/integrations/django/__init__.py +25 -46
  29. sentry_sdk/integrations/django/asgi.py +6 -2
  30. sentry_sdk/integrations/django/caching.py +13 -22
  31. sentry_sdk/integrations/django/middleware.py +1 -0
  32. sentry_sdk/integrations/django/signals_handlers.py +3 -1
  33. sentry_sdk/integrations/django/templates.py +8 -12
  34. sentry_sdk/integrations/django/transactions.py +1 -6
  35. sentry_sdk/integrations/django/views.py +5 -2
  36. sentry_sdk/integrations/falcon.py +7 -25
  37. sentry_sdk/integrations/fastapi.py +3 -3
  38. sentry_sdk/integrations/flask.py +1 -1
  39. sentry_sdk/integrations/gcp.py +63 -38
  40. sentry_sdk/integrations/graphene.py +6 -13
  41. sentry_sdk/integrations/grpc/aio/client.py +14 -8
  42. sentry_sdk/integrations/grpc/aio/server.py +19 -21
  43. sentry_sdk/integrations/grpc/client.py +8 -6
  44. sentry_sdk/integrations/grpc/server.py +12 -14
  45. sentry_sdk/integrations/httpx.py +47 -12
  46. sentry_sdk/integrations/huey.py +26 -22
  47. sentry_sdk/integrations/huggingface_hub.py +1 -0
  48. sentry_sdk/integrations/langchain.py +22 -15
  49. sentry_sdk/integrations/litestar.py +4 -2
  50. sentry_sdk/integrations/logging.py +7 -2
  51. sentry_sdk/integrations/openai.py +2 -0
  52. sentry_sdk/integrations/pymongo.py +18 -25
  53. sentry_sdk/integrations/pyramid.py +1 -1
  54. sentry_sdk/integrations/quart.py +3 -3
  55. sentry_sdk/integrations/ray.py +23 -17
  56. sentry_sdk/integrations/redis/_async_common.py +29 -18
  57. sentry_sdk/integrations/redis/_sync_common.py +28 -19
  58. sentry_sdk/integrations/redis/modules/caches.py +13 -10
  59. sentry_sdk/integrations/redis/modules/queries.py +14 -11
  60. sentry_sdk/integrations/redis/rb.py +4 -4
  61. sentry_sdk/integrations/redis/redis.py +6 -6
  62. sentry_sdk/integrations/redis/redis_cluster.py +18 -18
  63. sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +4 -4
  64. sentry_sdk/integrations/redis/utils.py +64 -24
  65. sentry_sdk/integrations/rq.py +68 -23
  66. sentry_sdk/integrations/rust_tracing.py +28 -43
  67. sentry_sdk/integrations/sanic.py +23 -13
  68. sentry_sdk/integrations/socket.py +9 -5
  69. sentry_sdk/integrations/sqlalchemy.py +8 -8
  70. sentry_sdk/integrations/starlette.py +11 -31
  71. sentry_sdk/integrations/starlite.py +4 -2
  72. sentry_sdk/integrations/stdlib.py +56 -9
  73. sentry_sdk/integrations/strawberry.py +40 -59
  74. sentry_sdk/integrations/threading.py +10 -26
  75. sentry_sdk/integrations/tornado.py +57 -18
  76. sentry_sdk/integrations/trytond.py +4 -1
  77. sentry_sdk/integrations/wsgi.py +84 -38
  78. sentry_sdk/opentelemetry/__init__.py +9 -0
  79. sentry_sdk/opentelemetry/consts.py +33 -0
  80. sentry_sdk/opentelemetry/contextvars_context.py +81 -0
  81. sentry_sdk/{integrations/opentelemetry → opentelemetry}/propagator.py +19 -28
  82. sentry_sdk/opentelemetry/sampler.py +326 -0
  83. sentry_sdk/opentelemetry/scope.py +218 -0
  84. sentry_sdk/opentelemetry/span_processor.py +335 -0
  85. sentry_sdk/opentelemetry/tracing.py +59 -0
  86. sentry_sdk/opentelemetry/utils.py +484 -0
  87. sentry_sdk/profiler/__init__.py +0 -40
  88. sentry_sdk/profiler/continuous_profiler.py +1 -30
  89. sentry_sdk/profiler/transaction_profiler.py +5 -56
  90. sentry_sdk/scope.py +108 -361
  91. sentry_sdk/sessions.py +0 -87
  92. sentry_sdk/tracing.py +415 -1161
  93. sentry_sdk/tracing_utils.py +130 -166
  94. sentry_sdk/transport.py +4 -104
  95. sentry_sdk/utils.py +169 -152
  96. {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/METADATA +3 -5
  97. sentry_sdk-3.0.0a2.dist-info/RECORD +154 -0
  98. sentry_sdk-3.0.0a2.dist-info/entry_points.txt +2 -0
  99. sentry_sdk/hub.py +0 -739
  100. sentry_sdk/integrations/opentelemetry/__init__.py +0 -7
  101. sentry_sdk/integrations/opentelemetry/consts.py +0 -5
  102. sentry_sdk/integrations/opentelemetry/integration.py +0 -58
  103. sentry_sdk/integrations/opentelemetry/span_processor.py +0 -391
  104. sentry_sdk/metrics.py +0 -965
  105. sentry_sdk-2.30.0.dist-info/RECORD +0 -152
  106. sentry_sdk-2.30.0.dist-info/entry_points.txt +0 -2
  107. {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/WHEEL +0 -0
  108. {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/licenses/LICENSE +0 -0
  109. {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/top_level.txt +0 -0
@@ -32,16 +32,14 @@ Each native extension requires its own integration.
32
32
 
33
33
  import json
34
34
  from enum import Enum, auto
35
- from typing import Any, Callable, Dict, Tuple, Optional
35
+ from typing import Any, Callable, Dict, Optional
36
36
 
37
37
  import sentry_sdk
38
38
  from sentry_sdk.integrations import Integration
39
39
  from sentry_sdk.scope import should_send_default_pii
40
- from sentry_sdk.tracing import Span as SentrySpan
40
+ from sentry_sdk.tracing import Span
41
41
  from sentry_sdk.utils import SENSITIVE_DATA_SUBSTITUTE
42
42
 
43
- TraceState = Optional[Tuple[Optional[SentrySpan], SentrySpan]]
44
-
45
43
 
46
44
  class RustTracingLevel(Enum):
47
45
  Trace = "TRACE"
@@ -171,7 +169,7 @@ class RustTracingLayer:
171
169
  else self.include_tracing_fields
172
170
  )
173
171
 
174
- def on_event(self, event: str, _span_state: TraceState) -> None:
172
+ def on_event(self, event: str, _span_state: Optional[Span]) -> None:
175
173
  deserialized_event = json.loads(event)
176
174
  metadata = deserialized_event.get("metadata", {})
177
175
 
@@ -185,7 +183,7 @@ class RustTracingLayer:
185
183
  elif event_type == EventTypeMapping.Event:
186
184
  process_event(deserialized_event)
187
185
 
188
- def on_new_span(self, attrs: str, span_id: str) -> TraceState:
186
+ def on_new_span(self, attrs: str, span_id: str) -> Optional[Span]:
189
187
  attrs = json.loads(attrs)
190
188
  metadata = attrs.get("metadata", {})
191
189
 
@@ -205,48 +203,35 @@ class RustTracingLayer:
205
203
  else:
206
204
  sentry_span_name = "<unknown>"
207
205
 
208
- kwargs = {
209
- "op": "function",
210
- "name": sentry_span_name,
211
- "origin": self.origin,
212
- }
213
-
214
- scope = sentry_sdk.get_current_scope()
215
- parent_sentry_span = scope.span
216
- if parent_sentry_span:
217
- sentry_span = parent_sentry_span.start_child(**kwargs)
218
- else:
219
- sentry_span = scope.start_span(**kwargs)
206
+ span = sentry_sdk.start_span(
207
+ op="function",
208
+ name=sentry_span_name,
209
+ origin=self.origin,
210
+ only_if_parent=True,
211
+ )
212
+ span.__enter__()
220
213
 
221
214
  fields = metadata.get("fields", [])
222
215
  for field in fields:
223
216
  if self._include_tracing_fields():
224
- sentry_span.set_data(field, attrs.get(field))
225
- else:
226
- sentry_span.set_data(field, SENSITIVE_DATA_SUBSTITUTE)
227
-
228
- scope.span = sentry_span
229
- return (parent_sentry_span, sentry_span)
230
-
231
- def on_close(self, span_id: str, span_state: TraceState) -> None:
232
- if span_state is None:
233
- return
234
-
235
- parent_sentry_span, sentry_span = span_state
236
- sentry_span.finish()
237
- sentry_sdk.get_current_scope().span = parent_sentry_span
238
-
239
- def on_record(self, span_id: str, values: str, span_state: TraceState) -> None:
240
- if span_state is None:
241
- return
242
- _parent_sentry_span, sentry_span = span_state
243
-
244
- deserialized_values = json.loads(values)
245
- for key, value in deserialized_values.items():
246
- if self._include_tracing_fields():
247
- sentry_span.set_data(key, value)
217
+ span.set_attribute(field, attrs.get(field))
248
218
  else:
249
- sentry_span.set_data(key, SENSITIVE_DATA_SUBSTITUTE)
219
+ span.set_attribute(field, SENSITIVE_DATA_SUBSTITUTE)
220
+
221
+ return span
222
+
223
+ def on_close(self, span_id: str, span: Optional[Span]) -> None:
224
+ if span is not None:
225
+ span.__exit__(None, None, None)
226
+
227
+ def on_record(self, span_id: str, values: str, span: Optional[Span]) -> None:
228
+ if span is not None:
229
+ deserialized_values = json.loads(values)
230
+ for key, value in deserialized_values.items():
231
+ if self._include_tracing_fields():
232
+ span.set_attribute(key, value)
233
+ else:
234
+ span.set_attribute(key, SENSITIVE_DATA_SUBSTITUTE)
250
235
 
251
236
 
252
237
  class RustTracingIntegration(Integration):
@@ -4,7 +4,6 @@ from inspect import isawaitable
4
4
  from urllib.parse import urlsplit
5
5
 
6
6
  import sentry_sdk
7
- from sentry_sdk import continue_trace
8
7
  from sentry_sdk.consts import OP
9
8
  from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
10
9
  from sentry_sdk.integrations._wsgi_common import RequestExtractor, _filter_headers
@@ -182,21 +181,25 @@ async def _context_enter(request):
182
181
  return
183
182
 
184
183
  weak_request = weakref.ref(request)
185
- request.ctx._sentry_scope = sentry_sdk.isolation_scope()
186
- scope = request.ctx._sentry_scope.__enter__()
184
+ request.ctx._sentry_scope_manager = sentry_sdk.isolation_scope()
185
+ scope = request.ctx._sentry_scope_manager.__enter__()
186
+ request.ctx._sentry_scope = scope
187
+
188
+ scope.set_transaction_name(request.path, TransactionSource.URL)
187
189
  scope.clear_breadcrumbs()
188
190
  scope.add_event_processor(_make_request_processor(weak_request))
189
191
 
190
- transaction = continue_trace(
191
- dict(request.headers),
192
+ # TODO-neel-potel test if this works
193
+ request.ctx._sentry_continue_trace = sentry_sdk.continue_trace(
194
+ dict(request.headers)
195
+ )
196
+ request.ctx._sentry_continue_trace.__enter__()
197
+ request.ctx._sentry_transaction = sentry_sdk.start_span(
192
198
  op=OP.HTTP_SERVER,
193
199
  # Unless the request results in a 404 error, the name and source will get overwritten in _set_transaction
194
200
  name=request.path,
195
201
  source=TransactionSource.URL,
196
202
  origin=SanicIntegration.origin,
197
- )
198
- request.ctx._sentry_transaction = sentry_sdk.start_transaction(
199
- transaction
200
203
  ).__enter__()
201
204
 
202
205
 
@@ -211,16 +214,23 @@ async def _context_exit(request, response=None):
211
214
  response_status = None if response is None else response.status
212
215
 
213
216
  # This capture_internal_exceptions block has been intentionally nested here, so that in case an exception
214
- # happens while trying to end the transaction, we still attempt to exit the hub.
217
+ # happens while trying to end the transaction, we still attempt to exit the scope.
215
218
  with capture_internal_exceptions():
216
219
  request.ctx._sentry_transaction.set_http_status(response_status)
217
- request.ctx._sentry_transaction.sampled &= (
220
+
221
+ if (
218
222
  isinstance(integration, SanicIntegration)
219
- and response_status not in integration._unsampled_statuses
220
- )
223
+ and response_status in integration._unsampled_statuses
224
+ ):
225
+ # drop the event in an event processor
226
+ request.ctx._sentry_scope.add_event_processor(
227
+ lambda _event, _hint: None
228
+ )
229
+
221
230
  request.ctx._sentry_transaction.__exit__(None, None, None)
231
+ request.ctx._sentry_continue_trace.__exit__(None, None, None)
222
232
 
223
- request.ctx._sentry_scope.__exit__(None, None, None)
233
+ request.ctx._sentry_scope_manager.__exit__(None, None, None)
224
234
 
225
235
 
226
236
  async def _set_transaction(request, route, **_):
@@ -61,10 +61,13 @@ def _patch_create_connection():
61
61
  op=OP.SOCKET_CONNECTION,
62
62
  name=_get_span_description(address[0], address[1]),
63
63
  origin=SocketIntegration.origin,
64
+ only_if_parent=True,
64
65
  ) as span:
65
- span.set_data("address", address)
66
- span.set_data("timeout", timeout)
67
- span.set_data("source_address", source_address)
66
+ host, port = address
67
+ span.set_attribute("address.host", host)
68
+ span.set_attribute("address.port", port)
69
+ span.set_attribute("timeout", timeout)
70
+ span.set_attribute("source_address", source_address)
68
71
 
69
72
  return real_create_connection(
70
73
  address=address, timeout=timeout, source_address=source_address
@@ -87,9 +90,10 @@ def _patch_getaddrinfo():
87
90
  op=OP.SOCKET_DNS,
88
91
  name=_get_span_description(host, port),
89
92
  origin=SocketIntegration.origin,
93
+ only_if_parent=True,
90
94
  ) as span:
91
- span.set_data("host", host)
92
- span.set_data("port", port)
95
+ span.set_attribute("host", host)
96
+ span.set_attribute("port", port)
93
97
 
94
98
  return real_getaddrinfo(host, port, family, type, proto, flags)
95
99
 
@@ -68,15 +68,15 @@ def _after_cursor_execute(conn, cursor, statement, parameters, context, *args):
68
68
  context, "_sentry_sql_span_manager", None
69
69
  ) # type: Optional[ContextManager[Any]]
70
70
 
71
- if ctx_mgr is not None:
72
- context._sentry_sql_span_manager = None
73
- ctx_mgr.__exit__(None, None, None)
74
-
75
71
  span = getattr(context, "_sentry_sql_span", None) # type: Optional[Span]
76
72
  if span is not None:
77
73
  with capture_internal_exceptions():
78
74
  add_query_source(span)
79
75
 
76
+ if ctx_mgr is not None:
77
+ context._sentry_sql_span_manager = None
78
+ ctx_mgr.__exit__(None, None, None)
79
+
80
80
 
81
81
  def _handle_error(context, *args):
82
82
  # type: (Any, *Any) -> None
@@ -128,19 +128,19 @@ def _set_db_data(span, conn):
128
128
  # type: (Span, Any) -> None
129
129
  db_system = _get_db_system(conn.engine.name)
130
130
  if db_system is not None:
131
- span.set_data(SPANDATA.DB_SYSTEM, db_system)
131
+ span.set_attribute(SPANDATA.DB_SYSTEM, db_system)
132
132
 
133
133
  if conn.engine.url is None:
134
134
  return
135
135
 
136
136
  db_name = conn.engine.url.database
137
137
  if db_name is not None:
138
- span.set_data(SPANDATA.DB_NAME, db_name)
138
+ span.set_attribute(SPANDATA.DB_NAME, db_name)
139
139
 
140
140
  server_address = conn.engine.url.host
141
141
  if server_address is not None:
142
- span.set_data(SPANDATA.SERVER_ADDRESS, server_address)
142
+ span.set_attribute(SPANDATA.SERVER_ADDRESS, server_address)
143
143
 
144
144
  server_port = conn.engine.url.port
145
145
  if server_port is not None:
146
- span.set_data(SPANDATA.SERVER_PORT, server_port)
146
+ span.set_attribute(SPANDATA.SERVER_PORT, server_port)
@@ -1,12 +1,11 @@
1
1
  import asyncio
2
2
  import functools
3
- import warnings
4
3
  from collections.abc import Set
5
4
  from copy import deepcopy
6
5
  from json import JSONDecodeError
7
6
 
8
7
  import sentry_sdk
9
- from sentry_sdk.consts import OP
8
+ from sentry_sdk.consts import OP, SOURCE_FOR_STYLE, TransactionSource
10
9
  from sentry_sdk.integrations import (
11
10
  DidNotEnable,
12
11
  Integration,
@@ -14,16 +13,11 @@ from sentry_sdk.integrations import (
14
13
  )
15
14
  from sentry_sdk.integrations._wsgi_common import (
16
15
  DEFAULT_HTTP_METHODS_TO_CAPTURE,
17
- HttpCodeRangeContainer,
18
16
  _is_json_content_type,
19
17
  request_body_within_bounds,
20
18
  )
21
19
  from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
22
20
  from sentry_sdk.scope import should_send_default_pii
23
- from sentry_sdk.tracing import (
24
- SOURCE_FOR_STYLE,
25
- TransactionSource,
26
- )
27
21
  from sentry_sdk.utils import (
28
22
  AnnotatedValue,
29
23
  capture_internal_exceptions,
@@ -37,9 +31,9 @@ from sentry_sdk.utils import (
37
31
  from typing import TYPE_CHECKING
38
32
 
39
33
  if TYPE_CHECKING:
40
- from typing import Any, Awaitable, Callable, Container, Dict, Optional, Tuple, Union
34
+ from typing import Any, Awaitable, Callable, Dict, Optional, Tuple
41
35
 
42
- from sentry_sdk._types import Event, HttpStatusCodeRange
36
+ from sentry_sdk._types import Event
43
37
 
44
38
  try:
45
39
  import starlette # type: ignore
@@ -89,7 +83,7 @@ class StarletteIntegration(Integration):
89
83
  def __init__(
90
84
  self,
91
85
  transaction_style="url", # type: str
92
- failed_request_status_codes=_DEFAULT_FAILED_REQUEST_STATUS_CODES, # type: Union[Set[int], list[HttpStatusCodeRange], None]
86
+ failed_request_status_codes=_DEFAULT_FAILED_REQUEST_STATUS_CODES, # type: Set[int]
93
87
  middleware_spans=True, # type: bool
94
88
  http_methods_to_capture=DEFAULT_HTTP_METHODS_TO_CAPTURE, # type: tuple[str, ...]
95
89
  ):
@@ -103,24 +97,7 @@ class StarletteIntegration(Integration):
103
97
  self.middleware_spans = middleware_spans
104
98
  self.http_methods_to_capture = tuple(map(str.upper, http_methods_to_capture))
105
99
 
106
- if isinstance(failed_request_status_codes, Set):
107
- self.failed_request_status_codes = (
108
- failed_request_status_codes
109
- ) # type: Container[int]
110
- else:
111
- warnings.warn(
112
- "Passing a list or None for failed_request_status_codes is deprecated. "
113
- "Please pass a set of int instead.",
114
- DeprecationWarning,
115
- stacklevel=2,
116
- )
117
-
118
- if failed_request_status_codes is None:
119
- self.failed_request_status_codes = _DEFAULT_FAILED_REQUEST_STATUS_CODES
120
- else:
121
- self.failed_request_status_codes = HttpCodeRangeContainer(
122
- failed_request_status_codes
123
- )
100
+ self.failed_request_status_codes = failed_request_status_codes
124
101
 
125
102
  @staticmethod
126
103
  def setup_once():
@@ -164,6 +141,7 @@ def _enable_span_for_middleware(middleware_class):
164
141
  op=OP.MIDDLEWARE_STARLETTE,
165
142
  name=middleware_name,
166
143
  origin=StarletteIntegration.origin,
144
+ only_if_parent=True,
167
145
  ) as middleware_span:
168
146
  middleware_span.set_tag("starlette.middleware_name", middleware_name)
169
147
 
@@ -174,6 +152,7 @@ def _enable_span_for_middleware(middleware_class):
174
152
  op=OP.MIDDLEWARE_STARLETTE_RECEIVE,
175
153
  name=getattr(receive, "__qualname__", str(receive)),
176
154
  origin=StarletteIntegration.origin,
155
+ only_if_parent=True,
177
156
  ) as span:
178
157
  span.set_tag("starlette.middleware_name", middleware_name)
179
158
  return await receive(*args, **kwargs)
@@ -189,6 +168,7 @@ def _enable_span_for_middleware(middleware_class):
189
168
  op=OP.MIDDLEWARE_STARLETTE_SEND,
190
169
  name=getattr(send, "__qualname__", str(send)),
191
170
  origin=StarletteIntegration.origin,
171
+ only_if_parent=True,
192
172
  ) as span:
193
173
  span.set_tag("starlette.middleware_name", middleware_name)
194
174
  return await send(*args, **kwargs)
@@ -329,7 +309,7 @@ def _add_user_to_sentry_scope(scope):
329
309
  user_info.setdefault("email", starlette_user.email)
330
310
 
331
311
  sentry_scope = sentry_sdk.get_isolation_scope()
332
- sentry_scope.user = user_info
312
+ sentry_scope.set_user(user_info)
333
313
 
334
314
 
335
315
  def patch_authentication_middleware(middleware_class):
@@ -493,8 +473,8 @@ def patch_request_response():
493
473
  return old_func(*args, **kwargs)
494
474
 
495
475
  current_scope = sentry_sdk.get_current_scope()
496
- if current_scope.transaction is not None:
497
- current_scope.transaction.update_active_thread()
476
+ if current_scope.root_span is not None:
477
+ current_scope.root_span.update_active_thread()
498
478
 
499
479
  sentry_scope = sentry_sdk.get_isolation_scope()
500
480
  if sentry_scope.profile is not None:
@@ -1,9 +1,8 @@
1
1
  import sentry_sdk
2
- from sentry_sdk.consts import OP
2
+ from sentry_sdk.consts import OP, SOURCE_FOR_STYLE, TransactionSource
3
3
  from sentry_sdk.integrations import DidNotEnable, Integration
4
4
  from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
5
5
  from sentry_sdk.scope import should_send_default_pii
6
- from sentry_sdk.tracing import SOURCE_FOR_STYLE, TransactionSource
7
6
  from sentry_sdk.utils import (
8
7
  ensure_integration_enabled,
9
8
  event_from_exception,
@@ -140,6 +139,7 @@ def enable_span_for_middleware(middleware):
140
139
  op=OP.MIDDLEWARE_STARLITE,
141
140
  name=middleware_name,
142
141
  origin=StarliteIntegration.origin,
142
+ only_if_parent=True,
143
143
  ) as middleware_span:
144
144
  middleware_span.set_tag("starlite.middleware_name", middleware_name)
145
145
 
@@ -152,6 +152,7 @@ def enable_span_for_middleware(middleware):
152
152
  op=OP.MIDDLEWARE_STARLITE_RECEIVE,
153
153
  name=getattr(receive, "__qualname__", str(receive)),
154
154
  origin=StarliteIntegration.origin,
155
+ only_if_parent=True,
155
156
  ) as span:
156
157
  span.set_tag("starlite.middleware_name", middleware_name)
157
158
  return await receive(*args, **kwargs)
@@ -169,6 +170,7 @@ def enable_span_for_middleware(middleware):
169
170
  op=OP.MIDDLEWARE_STARLITE_SEND,
170
171
  name=getattr(send, "__qualname__", str(send)),
171
172
  origin=StarliteIntegration.origin,
173
+ only_if_parent=True,
172
174
  ) as span:
173
175
  span.set_tag("starlite.middleware_name", middleware_name)
174
176
  return await send(message)
@@ -13,10 +13,13 @@ from sentry_sdk.utils import (
13
13
  SENSITIVE_DATA_SUBSTITUTE,
14
14
  capture_internal_exceptions,
15
15
  ensure_integration_enabled,
16
+ get_current_thread_meta,
17
+ http_client_status_to_breadcrumb_level,
16
18
  is_sentry_url,
17
19
  logger,
18
20
  safe_repr,
19
21
  parse_url,
22
+ set_thread_info_from_span,
20
23
  )
21
24
 
22
25
  from typing import TYPE_CHECKING
@@ -71,7 +74,7 @@ def _install_httplib():
71
74
 
72
75
  client = sentry_sdk.get_client()
73
76
  if client.get_integration(StdlibIntegration) is None or is_sentry_url(
74
- client, host
77
+ client, f"{host}:{port}" # noqa: E231
75
78
  ):
76
79
  return real_putrequest(self, method, url, *args, **kwargs)
77
80
 
@@ -93,12 +96,22 @@ def _install_httplib():
93
96
  name="%s %s"
94
97
  % (method, parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE),
95
98
  origin="auto.http.stdlib.httplib",
99
+ only_if_parent=True,
96
100
  )
97
- span.set_data(SPANDATA.HTTP_METHOD, method)
101
+ span.__enter__()
102
+
103
+ data = {
104
+ SPANDATA.HTTP_METHOD: method,
105
+ }
106
+ set_thread_info_from_span(data, span)
107
+
98
108
  if parsed_url is not None:
99
- span.set_data("url", parsed_url.url)
100
- span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
101
- span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
109
+ data["url"] = parsed_url.url
110
+ data[SPANDATA.HTTP_QUERY] = parsed_url.query
111
+ data[SPANDATA.HTTP_FRAGMENT] = parsed_url.fragment
112
+
113
+ for key, value in data.items():
114
+ span.set_attribute(key, value)
102
115
 
103
116
  rv = real_putrequest(self, method, url, *args, **kwargs)
104
117
 
@@ -117,6 +130,7 @@ def _install_httplib():
117
130
  self.putheader(key, value)
118
131
 
119
132
  self._sentrysdk_span = span # type: ignore[attr-defined]
133
+ self._sentrysdk_span_data = data # type: ignore[attr-defined]
120
134
 
121
135
  return rv
122
136
 
@@ -130,10 +144,22 @@ def _install_httplib():
130
144
  try:
131
145
  rv = real_getresponse(self, *args, **kwargs)
132
146
 
133
- span.set_http_status(int(rv.status))
134
- span.set_data("reason", rv.reason)
147
+ span_data = getattr(self, "_sentrysdk_span_data", {})
148
+ span_data[SPANDATA.HTTP_STATUS_CODE] = int(rv.status)
149
+ span_data["reason"] = rv.reason
150
+
151
+ status_code = int(rv.status)
152
+ span.set_http_status(status_code)
153
+ span.set_attribute("reason", rv.reason)
154
+
155
+ sentry_sdk.add_breadcrumb(
156
+ type="http",
157
+ category="httplib",
158
+ data=span_data,
159
+ level=http_client_status_to_breadcrumb_level(status_code),
160
+ )
135
161
  finally:
136
- span.finish()
162
+ span.__exit__(None, None, None)
137
163
 
138
164
  return rv
139
165
 
@@ -207,6 +233,7 @@ def _install_subprocess():
207
233
  op=OP.SUBPROCESS,
208
234
  name=description,
209
235
  origin="auto.subprocess.stdlib.subprocess",
236
+ only_if_parent=True,
210
237
  ) as span:
211
238
  for k, v in sentry_sdk.get_current_scope().iter_trace_propagation_headers(
212
239
  span=span
@@ -222,11 +249,29 @@ def _install_subprocess():
222
249
  env["SUBPROCESS_" + k.upper().replace("-", "_")] = v
223
250
 
224
251
  if cwd:
225
- span.set_data("subprocess.cwd", cwd)
252
+ span.set_attribute("subprocess.cwd", cwd)
226
253
 
227
254
  rv = old_popen_init(self, *a, **kw)
228
255
 
229
256
  span.set_tag("subprocess.pid", self.pid)
257
+
258
+ with capture_internal_exceptions():
259
+ thread_id, thread_name = get_current_thread_meta()
260
+ breadcrumb_data = {
261
+ "subprocess.pid": self.pid,
262
+ SPANDATA.THREAD_ID: thread_id,
263
+ SPANDATA.THREAD_NAME: thread_name,
264
+ }
265
+ if cwd:
266
+ breadcrumb_data["subprocess.cwd"] = cwd
267
+
268
+ sentry_sdk.add_breadcrumb(
269
+ type="subprocess",
270
+ category="subprocess",
271
+ message=description,
272
+ data=breadcrumb_data,
273
+ )
274
+
230
275
  return rv
231
276
 
232
277
  subprocess.Popen.__init__ = sentry_patched_popen_init # type: ignore
@@ -239,6 +284,7 @@ def _install_subprocess():
239
284
  with sentry_sdk.start_span(
240
285
  op=OP.SUBPROCESS_WAIT,
241
286
  origin="auto.subprocess.stdlib.subprocess",
287
+ only_if_parent=True,
242
288
  ) as span:
243
289
  span.set_tag("subprocess.pid", self.pid)
244
290
  return old_popen_wait(self, *a, **kw)
@@ -253,6 +299,7 @@ def _install_subprocess():
253
299
  with sentry_sdk.start_span(
254
300
  op=OP.SUBPROCESS_COMMUNICATE,
255
301
  origin="auto.subprocess.stdlib.subprocess",
302
+ only_if_parent=True,
256
303
  ) as span:
257
304
  span.set_tag("subprocess.pid", self.pid)
258
305
  return old_popen_communicate(self, *a, **kw)