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
@@ -1,14 +1,15 @@
1
1
  import sentry_sdk
2
- from sentry_sdk.consts import OP, SPANDATA
2
+ from sentry_sdk.consts import OP, SPANDATA, BAGGAGE_HEADER_NAME
3
3
  from sentry_sdk.integrations import Integration, DidNotEnable
4
- from sentry_sdk.tracing import BAGGAGE_HEADER_NAME
5
4
  from sentry_sdk.tracing_utils import Baggage, should_propagate_trace
6
5
  from sentry_sdk.utils import (
7
6
  SENSITIVE_DATA_SUBSTITUTE,
8
7
  capture_internal_exceptions,
9
8
  ensure_integration_enabled,
9
+ http_client_status_to_breadcrumb_level,
10
10
  logger,
11
11
  parse_url,
12
+ set_thread_info_from_span,
12
13
  )
13
14
 
14
15
  from typing import TYPE_CHECKING
@@ -60,12 +61,20 @@ def _install_httpx_client():
60
61
  parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE,
61
62
  ),
62
63
  origin=HttpxIntegration.origin,
64
+ only_if_parent=True,
63
65
  ) as span:
64
- span.set_data(SPANDATA.HTTP_METHOD, request.method)
66
+ data = {
67
+ SPANDATA.HTTP_METHOD: request.method,
68
+ }
69
+ set_thread_info_from_span(data, span)
70
+
65
71
  if parsed_url is not None:
66
- span.set_data("url", parsed_url.url)
67
- span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
68
- span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
72
+ data["url"] = parsed_url.url
73
+ data[SPANDATA.HTTP_QUERY] = parsed_url.query
74
+ data[SPANDATA.HTTP_FRAGMENT] = parsed_url.fragment
75
+
76
+ for key, value in data.items():
77
+ span.set_attribute(key, value)
69
78
 
70
79
  if should_propagate_trace(sentry_sdk.get_client(), str(request.url)):
71
80
  for (
@@ -86,7 +95,17 @@ def _install_httpx_client():
86
95
  rv = real_send(self, request, **kwargs)
87
96
 
88
97
  span.set_http_status(rv.status_code)
89
- span.set_data("reason", rv.reason_phrase)
98
+ span.set_attribute("reason", rv.reason_phrase)
99
+
100
+ data[SPANDATA.HTTP_STATUS_CODE] = rv.status_code
101
+ data["reason"] = rv.reason_phrase
102
+
103
+ sentry_sdk.add_breadcrumb(
104
+ type="http",
105
+ category="httplib",
106
+ data=data,
107
+ level=http_client_status_to_breadcrumb_level(rv.status_code),
108
+ )
90
109
 
91
110
  return rv
92
111
 
@@ -114,12 +133,18 @@ def _install_httpx_async_client():
114
133
  parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE,
115
134
  ),
116
135
  origin=HttpxIntegration.origin,
136
+ only_if_parent=True,
117
137
  ) as span:
118
- span.set_data(SPANDATA.HTTP_METHOD, request.method)
138
+ data = {
139
+ SPANDATA.HTTP_METHOD: request.method,
140
+ }
119
141
  if parsed_url is not None:
120
- span.set_data("url", parsed_url.url)
121
- span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
122
- span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
142
+ data["url"] = parsed_url.url
143
+ data[SPANDATA.HTTP_QUERY] = parsed_url.query
144
+ data[SPANDATA.HTTP_FRAGMENT] = parsed_url.fragment
145
+
146
+ for key, value in data.items():
147
+ span.set_attribute(key, value)
123
148
 
124
149
  if should_propagate_trace(sentry_sdk.get_client(), str(request.url)):
125
150
  for (
@@ -142,7 +167,17 @@ def _install_httpx_async_client():
142
167
  rv = await real_send(self, request, **kwargs)
143
168
 
144
169
  span.set_http_status(rv.status_code)
145
- span.set_data("reason", rv.reason_phrase)
170
+ span.set_attribute("reason", rv.reason_phrase)
171
+
172
+ data[SPANDATA.HTTP_STATUS_CODE] = rv.status_code
173
+ data["reason"] = rv.reason_phrase
174
+
175
+ sentry_sdk.add_breadcrumb(
176
+ type="http",
177
+ category="httplib",
178
+ data=data,
179
+ level=http_client_status_to_breadcrumb_level(rv.status_code),
180
+ )
146
181
 
147
182
  return rv
148
183
 
@@ -2,15 +2,16 @@ import sys
2
2
  from datetime import datetime
3
3
 
4
4
  import sentry_sdk
5
- from sentry_sdk.api import continue_trace, get_baggage, get_traceparent
6
- from sentry_sdk.consts import OP, SPANSTATUS
7
- from sentry_sdk.integrations import DidNotEnable, Integration
8
- from sentry_sdk.scope import should_send_default_pii
9
- from sentry_sdk.tracing import (
5
+ from sentry_sdk.api import get_baggage, get_traceparent
6
+ from sentry_sdk.consts import (
7
+ OP,
8
+ SPANSTATUS,
10
9
  BAGGAGE_HEADER_NAME,
11
10
  SENTRY_TRACE_HEADER_NAME,
12
11
  TransactionSource,
13
12
  )
13
+ from sentry_sdk.integrations import DidNotEnable, Integration
14
+ from sentry_sdk.scope import should_send_default_pii
14
15
  from sentry_sdk.utils import (
15
16
  capture_internal_exceptions,
16
17
  ensure_integration_enabled,
@@ -61,6 +62,7 @@ def patch_enqueue():
61
62
  op=OP.QUEUE_SUBMIT_HUEY,
62
63
  name=task.name,
63
64
  origin=HueyIntegration.origin,
65
+ only_if_parent=True,
64
66
  ):
65
67
  if not isinstance(task, PeriodicTask):
66
68
  # Attach trace propagation data to task kwargs. We do
@@ -109,11 +111,13 @@ def _capture_exception(exc_info):
109
111
  # type: (ExcInfo) -> None
110
112
  scope = sentry_sdk.get_current_scope()
111
113
 
112
- if exc_info[0] in HUEY_CONTROL_FLOW_EXCEPTIONS:
113
- scope.transaction.set_status(SPANSTATUS.ABORTED)
114
- return
114
+ if scope.root_span is not None:
115
+ if exc_info[0] in HUEY_CONTROL_FLOW_EXCEPTIONS:
116
+ scope.root_span.set_status(SPANSTATUS.ABORTED)
117
+ return
118
+
119
+ scope.root_span.set_status(SPANSTATUS.INTERNAL_ERROR)
115
120
 
116
- scope.transaction.set_status(SPANSTATUS.INTERNAL_ERROR)
117
121
  event, hint = event_from_exception(
118
122
  exc_info,
119
123
  client_options=sentry_sdk.get_client().options,
@@ -135,6 +139,10 @@ def _wrap_task_execute(func):
135
139
  _capture_exception(exc_info)
136
140
  reraise(*exc_info)
137
141
 
142
+ root_span = sentry_sdk.get_current_scope().root_span
143
+ if root_span is not None:
144
+ root_span.set_status(SPANSTATUS.OK)
145
+
138
146
  return result
139
147
 
140
148
  return _sentry_execute # type: ignore
@@ -153,22 +161,18 @@ def patch_execute():
153
161
  scope.clear_breadcrumbs()
154
162
  scope.add_event_processor(_make_event_processor(task))
155
163
 
156
- sentry_headers = task.kwargs.pop("sentry_headers", None)
157
-
158
- transaction = continue_trace(
159
- sentry_headers or {},
160
- name=task.name,
161
- op=OP.QUEUE_TASK_HUEY,
162
- source=TransactionSource.TASK,
163
- origin=HueyIntegration.origin,
164
- )
165
- transaction.set_status(SPANSTATUS.OK)
166
-
167
164
  if not getattr(task, "_sentry_is_patched", False):
168
165
  task.execute = _wrap_task_execute(task.execute)
169
166
  task._sentry_is_patched = True
170
167
 
171
- with sentry_sdk.start_transaction(transaction):
172
- return old_execute(self, task, timestamp)
168
+ sentry_headers = task.kwargs.pop("sentry_headers", {})
169
+ with sentry_sdk.continue_trace(sentry_headers):
170
+ with sentry_sdk.start_span(
171
+ name=task.name,
172
+ op=OP.QUEUE_TASK_HUEY,
173
+ source=TransactionSource.TASK,
174
+ origin=HueyIntegration.origin,
175
+ ):
176
+ return old_execute(self, task, timestamp)
173
177
 
174
178
  Huey._execute = _sentry_execute
@@ -77,6 +77,7 @@ def _wrap_text_generation(f):
77
77
  op=consts.OP.HUGGINGFACE_HUB_CHAT_COMPLETIONS_CREATE,
78
78
  name="Text Generation",
79
79
  origin=HuggingfaceHubIntegration.origin,
80
+ only_if_parent=True,
80
81
  )
81
82
  span.__enter__()
82
83
  try:
@@ -3,7 +3,7 @@ from functools import wraps
3
3
 
4
4
  import sentry_sdk
5
5
  from sentry_sdk.ai.monitoring import set_ai_pipeline_name, record_token_usage
6
- from sentry_sdk.consts import OP, SPANDATA
6
+ from sentry_sdk.consts import OP, SPANDATA, SPANSTATUS
7
7
  from sentry_sdk.ai.utils import set_data_normalized
8
8
  from sentry_sdk.scope import should_send_default_pii
9
9
  from sentry_sdk.tracing import Span
@@ -72,7 +72,6 @@ class LangchainIntegration(Integration):
72
72
 
73
73
 
74
74
  class WatchedSpan:
75
- span = None # type: Span
76
75
  num_completion_tokens = 0 # type: int
77
76
  num_prompt_tokens = 0 # type: int
78
77
  no_collect_tokens = False # type: bool
@@ -123,8 +122,9 @@ class SentryLangchainCallback(BaseCallbackHandler): # type: ignore[misc]
123
122
  span_data = self.span_map[run_id]
124
123
  if not span_data:
125
124
  return
126
- sentry_sdk.capture_exception(error, span_data.span.scope)
127
- span_data.span.__exit__(None, None, None)
125
+ sentry_sdk.capture_exception(error)
126
+ span_data.span.set_status(SPANSTATUS.INTERNAL_ERROR)
127
+ span_data.span.finish()
128
128
  del self.span_map[run_id]
129
129
 
130
130
  def _normalize_langchain_message(self, message):
@@ -136,21 +136,27 @@ class SentryLangchainCallback(BaseCallbackHandler): # type: ignore[misc]
136
136
  def _create_span(self, run_id, parent_id, **kwargs):
137
137
  # type: (SentryLangchainCallback, UUID, Optional[Any], Any) -> WatchedSpan
138
138
 
139
- watched_span = None # type: Optional[WatchedSpan]
140
- if parent_id:
141
- parent_span = self.span_map.get(parent_id) # type: Optional[WatchedSpan]
142
- if parent_span:
143
- watched_span = WatchedSpan(parent_span.span.start_child(**kwargs))
144
- parent_span.children.append(watched_span)
145
- if watched_span is None:
146
- watched_span = WatchedSpan(sentry_sdk.start_span(**kwargs))
139
+ parent_watched_span = self.span_map.get(parent_id) if parent_id else None
140
+ sentry_span = sentry_sdk.start_span(
141
+ parent_span=parent_watched_span.span if parent_watched_span else None,
142
+ only_if_parent=True,
143
+ **kwargs,
144
+ )
145
+ watched_span = WatchedSpan(sentry_span)
146
+ if parent_watched_span:
147
+ parent_watched_span.children.append(watched_span)
147
148
 
148
149
  if kwargs.get("op", "").startswith("ai.pipeline."):
149
150
  if kwargs.get("name"):
150
151
  set_ai_pipeline_name(kwargs.get("name"))
151
152
  watched_span.is_pipeline = True
152
153
 
153
- watched_span.span.__enter__()
154
+ # the same run_id is reused for the pipeline it seems
155
+ # so we need to end the older span to avoid orphan spans
156
+ existing_span_data = self.span_map.get(run_id)
157
+ if existing_span_data is not None:
158
+ self._exit_span(existing_span_data, run_id)
159
+
154
160
  self.span_map[run_id] = watched_span
155
161
  self.gc_span_map()
156
162
  return watched_span
@@ -161,7 +167,8 @@ class SentryLangchainCallback(BaseCallbackHandler): # type: ignore[misc]
161
167
  if span_data.is_pipeline:
162
168
  set_ai_pipeline_name(None)
163
169
 
164
- span_data.span.__exit__(None, None, None)
170
+ span_data.span.set_status(SPANSTATUS.OK)
171
+ span_data.span.finish()
165
172
  del self.span_map[run_id]
166
173
 
167
174
  def on_llm_start(
@@ -222,7 +229,7 @@ class SentryLangchainCallback(BaseCallbackHandler): # type: ignore[misc]
222
229
  if not model and "anthropic" in all_params.get("_type"):
223
230
  model = "claude-2"
224
231
  if model:
225
- span.set_data(SPANDATA.AI_MODEL_ID, model)
232
+ span.set_attribute(SPANDATA.AI_MODEL_ID, model)
226
233
  if should_send_default_pii() and self.include_prompts:
227
234
  set_data_normalized(
228
235
  span,
@@ -1,6 +1,6 @@
1
1
  from collections.abc import Set
2
2
  import sentry_sdk
3
- from sentry_sdk.consts import OP
3
+ from sentry_sdk.consts import OP, TransactionSource, SOURCE_FOR_STYLE
4
4
  from sentry_sdk.integrations import (
5
5
  _DEFAULT_FAILED_REQUEST_STATUS_CODES,
6
6
  DidNotEnable,
@@ -9,7 +9,6 @@ from sentry_sdk.integrations import (
9
9
  from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
10
10
  from sentry_sdk.integrations.logging import ignore_logger
11
11
  from sentry_sdk.scope import should_send_default_pii
12
- from sentry_sdk.tracing import TransactionSource, SOURCE_FOR_STYLE
13
12
  from sentry_sdk.utils import (
14
13
  ensure_integration_enabled,
15
14
  event_from_exception,
@@ -153,6 +152,7 @@ def enable_span_for_middleware(middleware):
153
152
  op=OP.MIDDLEWARE_LITESTAR,
154
153
  name=middleware_name,
155
154
  origin=LitestarIntegration.origin,
155
+ only_if_parent=True,
156
156
  ) as middleware_span:
157
157
  middleware_span.set_tag("litestar.middleware_name", middleware_name)
158
158
 
@@ -165,6 +165,7 @@ def enable_span_for_middleware(middleware):
165
165
  op=OP.MIDDLEWARE_LITESTAR_RECEIVE,
166
166
  name=getattr(receive, "__qualname__", str(receive)),
167
167
  origin=LitestarIntegration.origin,
168
+ only_if_parent=True,
168
169
  ) as span:
169
170
  span.set_tag("litestar.middleware_name", middleware_name)
170
171
  return await receive(*args, **kwargs)
@@ -182,6 +183,7 @@ def enable_span_for_middleware(middleware):
182
183
  op=OP.MIDDLEWARE_LITESTAR_SEND,
183
184
  name=getattr(send, "__qualname__", str(send)),
184
185
  origin=LitestarIntegration.origin,
186
+ only_if_parent=True,
185
187
  ) as span:
186
188
  span.set_tag("litestar.middleware_name", middleware_name)
187
189
  return await send(message)
@@ -25,7 +25,7 @@ if TYPE_CHECKING:
25
25
  from typing import Optional
26
26
 
27
27
  DEFAULT_LEVEL = logging.INFO
28
- DEFAULT_EVENT_LEVEL = logging.ERROR
28
+ DEFAULT_EVENT_LEVEL = None # None means no events are captured
29
29
  LOGGING_TO_EVENT_LEVEL = {
30
30
  logging.NOTSET: "notset",
31
31
  logging.DEBUG: "debug",
@@ -54,7 +54,12 @@ SEVERITY_TO_OTEL_SEVERITY = {
54
54
  # Note: Ignoring by logger name here is better than mucking with thread-locals.
55
55
  # We do not necessarily know whether thread-locals work 100% correctly in the user's environment.
56
56
  _IGNORED_LOGGERS = set(
57
- ["sentry_sdk.errors", "urllib3.connectionpool", "urllib3.connection"]
57
+ [
58
+ "sentry_sdk.errors",
59
+ "urllib3.connectionpool",
60
+ "urllib3.connection",
61
+ "opentelemetry.*",
62
+ ]
58
63
  )
59
64
 
60
65
 
@@ -139,6 +139,7 @@ def _new_chat_completion_common(f, *args, **kwargs):
139
139
  op=consts.OP.OPENAI_CHAT_COMPLETIONS_CREATE,
140
140
  name="Chat Completion",
141
141
  origin=OpenAIIntegration.origin,
142
+ only_if_parent=True,
142
143
  )
143
144
  span.__enter__()
144
145
 
@@ -324,6 +325,7 @@ def _new_embeddings_create_common(f, *args, **kwargs):
324
325
  op=consts.OP.OPENAI_EMBEDDINGS_CREATE,
325
326
  description="OpenAI Embedding Creation",
326
327
  origin=OpenAIIntegration.origin,
328
+ only_if_parent=True,
327
329
  ) as span:
328
330
  if "input" in kwargs and (
329
331
  should_send_default_pii() and integration.include_prompts
@@ -1,12 +1,11 @@
1
1
  import copy
2
- import json
3
2
 
4
3
  import sentry_sdk
5
4
  from sentry_sdk.consts import SPANSTATUS, SPANDATA, OP
6
5
  from sentry_sdk.integrations import DidNotEnable, Integration
7
6
  from sentry_sdk.scope import should_send_default_pii
8
7
  from sentry_sdk.tracing import Span
9
- from sentry_sdk.utils import capture_internal_exceptions
8
+ from sentry_sdk.utils import capture_internal_exceptions, _serialize_span_attribute
10
9
 
11
10
  try:
12
11
  from pymongo import monitoring
@@ -127,56 +126,50 @@ class CommandTracer(monitoring.CommandListener):
127
126
  command.pop("$clusterTime", None)
128
127
  command.pop("$signature", None)
129
128
 
130
- tags = {
131
- "db.name": event.database_name,
129
+ data = {
130
+ SPANDATA.DB_NAME: event.database_name,
132
131
  SPANDATA.DB_SYSTEM: "mongodb",
133
132
  SPANDATA.DB_OPERATION: event.command_name,
134
133
  SPANDATA.DB_MONGODB_COLLECTION: command.get(event.command_name),
135
134
  }
136
135
 
137
136
  try:
138
- tags["net.peer.name"] = event.connection_id[0]
139
- tags["net.peer.port"] = str(event.connection_id[1])
137
+ data["net.peer.name"] = event.connection_id[0]
138
+ data["net.peer.port"] = str(event.connection_id[1])
140
139
  except TypeError:
141
140
  pass
142
141
 
143
- data = {"operation_ids": {}} # type: Dict[str, Any]
144
- data["operation_ids"]["operation"] = event.operation_id
145
- data["operation_ids"]["request"] = event.request_id
146
-
147
- data.update(_get_db_data(event))
148
-
149
142
  try:
150
143
  lsid = command.pop("lsid")["id"]
151
- data["operation_ids"]["session"] = str(lsid)
144
+ data["session_id"] = str(lsid)
152
145
  except KeyError:
153
146
  pass
154
147
 
155
148
  if not should_send_default_pii():
156
149
  command = _strip_pii(command)
157
150
 
158
- query = json.dumps(command, default=str)
151
+ query = _serialize_span_attribute(command)
159
152
  span = sentry_sdk.start_span(
160
153
  op=OP.DB,
161
154
  name=query,
162
155
  origin=PyMongoIntegration.origin,
156
+ only_if_parent=True,
163
157
  )
164
158
 
165
- for tag, value in tags.items():
166
- # set the tag for backwards-compatibility.
167
- # TODO: remove the set_tag call in the next major release!
168
- span.set_tag(tag, value)
169
-
170
- span.set_data(tag, value)
171
-
172
- for key, value in data.items():
173
- span.set_data(key, value)
174
-
175
159
  with capture_internal_exceptions():
176
160
  sentry_sdk.add_breadcrumb(
177
- message=query, category="query", type=OP.DB, data=tags
161
+ message=query, category="query", type=OP.DB, data=data
178
162
  )
179
163
 
164
+ for key, value in data.items():
165
+ span.set_attribute(key, value)
166
+
167
+ for key, value in _get_db_data(event).items():
168
+ span.set_attribute(key, value)
169
+
170
+ span.set_attribute("operation_id", event.operation_id)
171
+ span.set_attribute("request_id", event.request_id)
172
+
180
173
  self._ongoing_operations[self._operation_key(event)] = span.__enter__()
181
174
 
182
175
  def failed(self, event):
@@ -4,11 +4,11 @@ import sys
4
4
  import weakref
5
5
 
6
6
  import sentry_sdk
7
+ from sentry_sdk.consts import SOURCE_FOR_STYLE
7
8
  from sentry_sdk.integrations import Integration, DidNotEnable
8
9
  from sentry_sdk.integrations._wsgi_common import RequestExtractor
9
10
  from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
10
11
  from sentry_sdk.scope import should_send_default_pii
11
- from sentry_sdk.tracing import SOURCE_FOR_STYLE
12
12
  from sentry_sdk.utils import (
13
13
  capture_internal_exceptions,
14
14
  ensure_integration_enabled,
@@ -3,11 +3,11 @@ import inspect
3
3
  from functools import wraps
4
4
 
5
5
  import sentry_sdk
6
+ from sentry_sdk.consts import SOURCE_FOR_STYLE
6
7
  from sentry_sdk.integrations import DidNotEnable, Integration
7
8
  from sentry_sdk.integrations._wsgi_common import _filter_headers
8
9
  from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
9
10
  from sentry_sdk.scope import should_send_default_pii
10
- from sentry_sdk.tracing import SOURCE_FOR_STYLE
11
11
  from sentry_sdk.utils import (
12
12
  capture_internal_exceptions,
13
13
  ensure_integration_enabled,
@@ -122,8 +122,8 @@ def patch_scaffold_route():
122
122
  def _sentry_func(*args, **kwargs):
123
123
  # type: (*Any, **Any) -> Any
124
124
  current_scope = sentry_sdk.get_current_scope()
125
- if current_scope.transaction is not None:
126
- current_scope.transaction.update_active_thread()
125
+ if current_scope.root_span is not None:
126
+ current_scope.root_span.update_active_thread()
127
127
 
128
128
  sentry_scope = sentry_sdk.get_isolation_scope()
129
129
  if sentry_scope.profile is not None:
@@ -26,6 +26,8 @@ if TYPE_CHECKING:
26
26
  from typing import Any, Optional
27
27
  from sentry_sdk.utils import ExcInfo
28
28
 
29
+ DEFAULT_TRANSACTION_NAME = "unknown Ray function"
30
+
29
31
 
30
32
  def _check_sentry_initialized():
31
33
  # type: () -> None
@@ -58,25 +60,28 @@ def _patch_ray_remote():
58
60
  """
59
61
  _check_sentry_initialized()
60
62
 
61
- transaction = sentry_sdk.continue_trace(
62
- _tracing or {},
63
- op=OP.QUEUE_TASK_RAY,
64
- name=qualname_from_function(f),
65
- origin=RayIntegration.origin,
63
+ root_span_name = qualname_from_function(f) or DEFAULT_TRANSACTION_NAME
64
+ sentry_sdk.get_current_scope().set_transaction_name(
65
+ root_span_name,
66
66
  source=TransactionSource.TASK,
67
67
  )
68
-
69
- with sentry_sdk.start_transaction(transaction) as transaction:
70
- try:
71
- result = f(*f_args, **f_kwargs)
72
- transaction.set_status(SPANSTATUS.OK)
73
- except Exception:
74
- transaction.set_status(SPANSTATUS.INTERNAL_ERROR)
75
- exc_info = sys.exc_info()
76
- _capture_exception(exc_info)
77
- reraise(*exc_info)
78
-
79
- return result
68
+ with sentry_sdk.continue_trace(_tracing or {}):
69
+ with sentry_sdk.start_span(
70
+ op=OP.QUEUE_TASK_RAY,
71
+ name=root_span_name,
72
+ origin=RayIntegration.origin,
73
+ source=TransactionSource.TASK,
74
+ ) as root_span:
75
+ try:
76
+ result = f(*f_args, **f_kwargs)
77
+ root_span.set_status(SPANSTATUS.OK)
78
+ except Exception:
79
+ root_span.set_status(SPANSTATUS.INTERNAL_ERROR)
80
+ exc_info = sys.exc_info()
81
+ _capture_exception(exc_info)
82
+ reraise(*exc_info)
83
+
84
+ return result
80
85
 
81
86
  rv = old_remote(_f, *args, *kwargs)
82
87
  old_remote_method = rv.remote
@@ -90,6 +95,7 @@ def _patch_ray_remote():
90
95
  op=OP.QUEUE_SUBMIT_RAY,
91
96
  name=qualname_from_function(f),
92
97
  origin=RayIntegration.origin,
98
+ only_if_parent=True,
93
99
  ) as span:
94
100
  tracing = {
95
101
  k: v
@@ -3,14 +3,15 @@ from sentry_sdk.consts import OP
3
3
  from sentry_sdk.integrations.redis.consts import SPAN_ORIGIN
4
4
  from sentry_sdk.integrations.redis.modules.caches import (
5
5
  _compile_cache_span_properties,
6
- _set_cache_data,
6
+ _get_cache_data,
7
7
  )
8
8
  from sentry_sdk.integrations.redis.modules.queries import _compile_db_span_properties
9
9
  from sentry_sdk.integrations.redis.utils import (
10
- _set_client_data,
11
- _set_pipeline_data,
10
+ _create_breadcrumb,
11
+ _get_client_data,
12
+ _get_pipeline_data,
13
+ _update_span,
12
14
  )
13
- from sentry_sdk.tracing import Span
14
15
  from sentry_sdk.utils import capture_internal_exceptions
15
16
 
16
17
  from typing import TYPE_CHECKING
@@ -23,9 +24,9 @@ if TYPE_CHECKING:
23
24
 
24
25
 
25
26
  def patch_redis_async_pipeline(
26
- pipeline_cls, is_cluster, get_command_args_fn, set_db_data_fn
27
+ pipeline_cls, is_cluster, get_command_args_fn, get_db_data_fn
27
28
  ):
28
- # type: (Union[type[Pipeline[Any]], type[ClusterPipeline[Any]]], bool, Any, Callable[[Span, Any], None]) -> None
29
+ # type: (Union[type[Pipeline[Any]], type[ClusterPipeline[Any]]], bool, Any, Callable[[Any], dict[str, Any]]) -> None
29
30
  old_execute = pipeline_cls.execute
30
31
 
31
32
  from sentry_sdk.integrations.redis import RedisIntegration
@@ -39,8 +40,11 @@ def patch_redis_async_pipeline(
39
40
  op=OP.DB_REDIS,
40
41
  name="redis.pipeline.execute",
41
42
  origin=SPAN_ORIGIN,
43
+ only_if_parent=True,
42
44
  ) as span:
43
45
  with capture_internal_exceptions():
46
+ span_data = get_db_data_fn(self)
47
+
44
48
  try:
45
49
  command_seq = self._execution_strategy._command_queue
46
50
  except AttributeError:
@@ -49,22 +53,22 @@ def patch_redis_async_pipeline(
49
53
  else:
50
54
  command_seq = self.command_stack
51
55
 
52
- set_db_data_fn(span, self)
53
- _set_pipeline_data(
54
- span,
55
- is_cluster,
56
- get_command_args_fn,
57
- False if is_cluster else self.is_transaction,
58
- command_seq,
56
+ pipeline_data = _get_pipeline_data(
57
+ is_cluster=is_cluster,
58
+ get_command_args_fn=get_command_args_fn,
59
+ is_transaction=False if is_cluster else self.is_transaction,
60
+ command_seq=command_seq,
59
61
  )
62
+ _update_span(span, span_data, pipeline_data)
63
+ _create_breadcrumb("redis.pipeline.execute", span_data, pipeline_data)
60
64
 
61
65
  return await old_execute(self, *args, **kwargs)
62
66
 
63
67
  pipeline_cls.execute = _sentry_execute # type: ignore
64
68
 
65
69
 
66
- def patch_redis_async_client(cls, is_cluster, set_db_data_fn):
67
- # type: (Union[type[StrictRedis[Any]], type[RedisCluster[Any]]], bool, Callable[[Span, Any], None]) -> None
70
+ def patch_redis_async_client(cls, is_cluster, get_db_data_fn):
71
+ # type: (Union[type[StrictRedis[Any]], type[RedisCluster[Any]]], bool, Callable[[Any], dict[str, Any]]) -> None
68
72
  old_execute_command = cls.execute_command
69
73
 
70
74
  from sentry_sdk.integrations.redis import RedisIntegration
@@ -88,6 +92,7 @@ def patch_redis_async_client(cls, is_cluster, set_db_data_fn):
88
92
  op=cache_properties["op"],
89
93
  name=cache_properties["description"],
90
94
  origin=SPAN_ORIGIN,
95
+ only_if_parent=True,
91
96
  )
92
97
  cache_span.__enter__()
93
98
 
@@ -97,18 +102,24 @@ def patch_redis_async_client(cls, is_cluster, set_db_data_fn):
97
102
  op=db_properties["op"],
98
103
  name=db_properties["description"],
99
104
  origin=SPAN_ORIGIN,
105
+ only_if_parent=True,
100
106
  )
101
107
  db_span.__enter__()
102
108
 
103
- set_db_data_fn(db_span, self)
104
- _set_client_data(db_span, is_cluster, name, *args)
109
+ db_span_data = get_db_data_fn(self)
110
+ db_client_span_data = _get_client_data(is_cluster, name, *args)
111
+ _update_span(db_span, db_span_data, db_client_span_data)
112
+ _create_breadcrumb(
113
+ db_properties["description"], db_span_data, db_client_span_data
114
+ )
105
115
 
106
116
  value = await old_execute_command(self, name, *args, **kwargs)
107
117
 
108
118
  db_span.__exit__(None, None, None)
109
119
 
110
120
  if cache_span:
111
- _set_cache_data(cache_span, self, cache_properties, value)
121
+ cache_span_data = _get_cache_data(self, cache_properties, value)
122
+ _update_span(cache_span, cache_span_data)
112
123
  cache_span.__exit__(None, None, None)
113
124
 
114
125
  return value