sentry-sdk 2.27.0__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 (110) 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 +2 -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 +2 -2
  14. sentry_sdk/integrations/__init__.py +4 -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 +102 -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/litestar.py +4 -2
  51. sentry_sdk/integrations/logging.py +12 -3
  52. sentry_sdk/integrations/openai.py +2 -0
  53. sentry_sdk/integrations/pymongo.py +18 -25
  54. sentry_sdk/integrations/pyramid.py +1 -1
  55. sentry_sdk/integrations/quart.py +3 -3
  56. sentry_sdk/integrations/ray.py +23 -17
  57. sentry_sdk/integrations/redis/_async_common.py +30 -18
  58. sentry_sdk/integrations/redis/_sync_common.py +28 -18
  59. sentry_sdk/integrations/redis/modules/caches.py +13 -10
  60. sentry_sdk/integrations/redis/modules/queries.py +14 -11
  61. sentry_sdk/integrations/redis/rb.py +4 -4
  62. sentry_sdk/integrations/redis/redis.py +6 -6
  63. sentry_sdk/integrations/redis/redis_cluster.py +18 -16
  64. sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +4 -4
  65. sentry_sdk/integrations/redis/utils.py +63 -19
  66. sentry_sdk/integrations/rq.py +68 -23
  67. sentry_sdk/integrations/rust_tracing.py +28 -43
  68. sentry_sdk/integrations/sanic.py +23 -13
  69. sentry_sdk/integrations/socket.py +9 -5
  70. sentry_sdk/integrations/sqlalchemy.py +8 -8
  71. sentry_sdk/integrations/starlette.py +11 -31
  72. sentry_sdk/integrations/starlite.py +4 -2
  73. sentry_sdk/integrations/stdlib.py +56 -9
  74. sentry_sdk/integrations/strawberry.py +40 -59
  75. sentry_sdk/integrations/threading.py +10 -26
  76. sentry_sdk/integrations/tornado.py +57 -18
  77. sentry_sdk/integrations/trytond.py +4 -1
  78. sentry_sdk/integrations/wsgi.py +84 -38
  79. sentry_sdk/opentelemetry/__init__.py +9 -0
  80. sentry_sdk/opentelemetry/consts.py +33 -0
  81. sentry_sdk/opentelemetry/contextvars_context.py +73 -0
  82. sentry_sdk/{integrations/opentelemetry → opentelemetry}/propagator.py +19 -28
  83. sentry_sdk/opentelemetry/sampler.py +326 -0
  84. sentry_sdk/opentelemetry/scope.py +218 -0
  85. sentry_sdk/opentelemetry/span_processor.py +329 -0
  86. sentry_sdk/opentelemetry/tracing.py +35 -0
  87. sentry_sdk/opentelemetry/utils.py +476 -0
  88. sentry_sdk/profiler/__init__.py +0 -40
  89. sentry_sdk/profiler/continuous_profiler.py +1 -30
  90. sentry_sdk/profiler/transaction_profiler.py +5 -56
  91. sentry_sdk/scope.py +107 -351
  92. sentry_sdk/sessions.py +0 -87
  93. sentry_sdk/tracing.py +418 -1144
  94. sentry_sdk/tracing_utils.py +126 -164
  95. sentry_sdk/transport.py +4 -104
  96. sentry_sdk/utils.py +169 -152
  97. {sentry_sdk-2.27.0.dist-info → sentry_sdk-3.0.0a1.dist-info}/METADATA +3 -5
  98. sentry_sdk-3.0.0a1.dist-info/RECORD +154 -0
  99. {sentry_sdk-2.27.0.dist-info → sentry_sdk-3.0.0a1.dist-info}/WHEEL +1 -1
  100. sentry_sdk-3.0.0a1.dist-info/entry_points.txt +2 -0
  101. sentry_sdk/hub.py +0 -739
  102. sentry_sdk/integrations/opentelemetry/__init__.py +0 -7
  103. sentry_sdk/integrations/opentelemetry/consts.py +0 -5
  104. sentry_sdk/integrations/opentelemetry/integration.py +0 -58
  105. sentry_sdk/integrations/opentelemetry/span_processor.py +0 -391
  106. sentry_sdk/metrics.py +0 -965
  107. sentry_sdk-2.27.0.dist-info/RECORD +0 -152
  108. sentry_sdk-2.27.0.dist-info/entry_points.txt +0 -2
  109. {sentry_sdk-2.27.0.dist-info → sentry_sdk-3.0.0a1.dist-info}/licenses/LICENSE +0 -0
  110. {sentry_sdk-2.27.0.dist-info → sentry_sdk-3.0.0a1.dist-info}/top_level.txt +0 -0
@@ -5,7 +5,7 @@ from sentry_sdk.consts import OP, SPANSTATUS
5
5
  from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
6
6
  from sentry_sdk.integrations.logging import ignore_logger
7
7
  from sentry_sdk.scope import should_send_default_pii
8
- from sentry_sdk.tracing import Transaction, TransactionSource
8
+ from sentry_sdk.tracing import TransactionSource
9
9
  from sentry_sdk.utils import (
10
10
  capture_internal_exceptions,
11
11
  ensure_integration_enabled,
@@ -37,6 +37,8 @@ if TYPE_CHECKING:
37
37
 
38
38
  ARQ_CONTROL_FLOW_EXCEPTIONS = (JobExecutionFailed, Retry, RetryJob)
39
39
 
40
+ DEFAULT_TRANSACTION_NAME = "unknown arq task"
41
+
40
42
 
41
43
  class ArqIntegration(Integration):
42
44
  identifier = "arq"
@@ -76,7 +78,10 @@ def patch_enqueue_job():
76
78
  return await old_enqueue_job(self, function, *args, **kwargs)
77
79
 
78
80
  with sentry_sdk.start_span(
79
- op=OP.QUEUE_SUBMIT_ARQ, name=function, origin=ArqIntegration.origin
81
+ op=OP.QUEUE_SUBMIT_ARQ,
82
+ name=function,
83
+ origin=ArqIntegration.origin,
84
+ only_if_parent=True,
80
85
  ):
81
86
  return await old_enqueue_job(self, function, *args, **kwargs)
82
87
 
@@ -96,18 +101,24 @@ def patch_run_job():
96
101
 
97
102
  with sentry_sdk.isolation_scope() as scope:
98
103
  scope._name = "arq"
104
+ scope.set_transaction_name(
105
+ DEFAULT_TRANSACTION_NAME,
106
+ source=TransactionSource.TASK,
107
+ )
99
108
  scope.clear_breadcrumbs()
100
109
 
101
- transaction = Transaction(
102
- name="unknown arq task",
103
- status="ok",
110
+ with sentry_sdk.start_span(
104
111
  op=OP.QUEUE_TASK_ARQ,
112
+ name=DEFAULT_TRANSACTION_NAME,
105
113
  source=TransactionSource.TASK,
106
114
  origin=ArqIntegration.origin,
107
- )
115
+ ) as span:
116
+ return_value = await old_run_job(self, job_id, score)
117
+
118
+ if span.status is None:
119
+ span.set_status(SPANSTATUS.OK)
108
120
 
109
- with sentry_sdk.start_transaction(transaction):
110
- return await old_run_job(self, job_id, score)
121
+ return return_value
111
122
 
112
123
  Worker.run_job = _sentry_run_job
113
124
 
@@ -116,12 +127,12 @@ def _capture_exception(exc_info):
116
127
  # type: (ExcInfo) -> None
117
128
  scope = sentry_sdk.get_current_scope()
118
129
 
119
- if scope.transaction is not None:
130
+ if scope.root_span is not None:
120
131
  if exc_info[0] in ARQ_CONTROL_FLOW_EXCEPTIONS:
121
- scope.transaction.set_status(SPANSTATUS.ABORTED)
132
+ scope.root_span.set_status(SPANSTATUS.ABORTED)
122
133
  return
123
134
 
124
- scope.transaction.set_status(SPANSTATUS.INTERNAL_ERROR)
135
+ scope.root_span.set_status(SPANSTATUS.INTERNAL_ERROR)
125
136
 
126
137
  event, hint = event_from_exception(
127
138
  exc_info,
@@ -138,8 +149,8 @@ def _make_event_processor(ctx, *args, **kwargs):
138
149
 
139
150
  with capture_internal_exceptions():
140
151
  scope = sentry_sdk.get_current_scope()
141
- if scope.transaction is not None:
142
- scope.transaction.name = ctx["job_name"]
152
+ if scope.root_span is not None:
153
+ scope.root_span.name = ctx["job_name"]
143
154
  event["transaction"] = ctx["job_name"]
144
155
 
145
156
  tags = event.setdefault("tags", {})
@@ -10,25 +10,22 @@ from copy import deepcopy
10
10
  from functools import partial
11
11
 
12
12
  import sentry_sdk
13
- from sentry_sdk.api import continue_trace
14
- from sentry_sdk.consts import OP
13
+ from sentry_sdk.consts import OP, SOURCE_FOR_STYLE, TransactionSource
15
14
 
16
15
  from sentry_sdk.integrations._asgi_common import (
17
16
  _get_headers,
17
+ _get_query,
18
18
  _get_request_data,
19
19
  _get_url,
20
20
  )
21
21
  from sentry_sdk.integrations._wsgi_common import (
22
22
  DEFAULT_HTTP_METHODS_TO_CAPTURE,
23
- nullcontext,
23
+ _request_headers_to_span_attributes,
24
24
  )
25
25
  from sentry_sdk.sessions import track_session
26
- from sentry_sdk.tracing import (
27
- SOURCE_FOR_STYLE,
28
- TransactionSource,
29
- )
30
26
  from sentry_sdk.utils import (
31
27
  ContextVar,
28
+ capture_internal_exceptions,
32
29
  event_from_exception,
33
30
  HAS_REAL_CONTEXTVARS,
34
31
  CONTEXTVARS_ERROR_MESSAGE,
@@ -36,7 +33,6 @@ from sentry_sdk.utils import (
36
33
  transaction_from_function,
37
34
  _get_installed_modules,
38
35
  )
39
- from sentry_sdk.tracing import Transaction
40
36
 
41
37
  from typing import TYPE_CHECKING
42
38
 
@@ -56,6 +52,14 @@ _DEFAULT_TRANSACTION_NAME = "generic ASGI request"
56
52
 
57
53
  TRANSACTION_STYLE_VALUES = ("endpoint", "url")
58
54
 
55
+ ASGI_SCOPE_PROPERTY_TO_ATTRIBUTE = {
56
+ "http_version": "network.protocol.version",
57
+ "method": "http.request.method",
58
+ "path": "url.path",
59
+ "scheme": "url.scheme",
60
+ "type": "network.protocol.name",
61
+ }
62
+
59
63
 
60
64
  def _capture_exception(exc, mechanism_type="asgi"):
61
65
  # type: (Any, str) -> None
@@ -100,7 +104,7 @@ class SentryAsgiMiddleware:
100
104
  unsafe_context_data=False, # type: bool
101
105
  transaction_style="endpoint", # type: str
102
106
  mechanism_type="asgi", # type: str
103
- span_origin="manual", # type: str
107
+ span_origin=None, # type: Optional[str]
104
108
  http_methods_to_capture=DEFAULT_HTTP_METHODS_TO_CAPTURE, # type: Tuple[str, ...]
105
109
  ):
106
110
  # type: (...) -> None
@@ -157,24 +161,40 @@ class SentryAsgiMiddleware:
157
161
  # type: (Any, Any, Any) -> Any
158
162
  return await self._run_app(scope, receive, send, asgi_version=3)
159
163
 
164
+ async def _run_original_app(self, scope, receive, send, asgi_version):
165
+ # type: (Any, Any, Any, Any, int) -> Any
166
+ try:
167
+ if asgi_version == 2:
168
+ return await self.app(scope)(receive, send)
169
+ else:
170
+ return await self.app(scope, receive, send)
171
+
172
+ except Exception as exc:
173
+ _capture_exception(exc, mechanism_type=self.mechanism_type)
174
+ raise exc from None
175
+
160
176
  async def _run_app(self, scope, receive, send, asgi_version):
161
177
  # type: (Any, Any, Any, Any, int) -> Any
162
178
  is_recursive_asgi_middleware = _asgi_middleware_applied.get(False)
163
179
  is_lifespan = scope["type"] == "lifespan"
164
180
  if is_recursive_asgi_middleware or is_lifespan:
165
- try:
166
- if asgi_version == 2:
167
- return await self.app(scope)(receive, send)
168
- else:
169
- return await self.app(scope, receive, send)
170
-
171
- except Exception as exc:
172
- _capture_exception(exc, mechanism_type=self.mechanism_type)
173
- raise exc from None
181
+ return await self._run_original_app(scope, receive, send, asgi_version)
174
182
 
175
183
  _asgi_middleware_applied.set(True)
176
184
  try:
177
185
  with sentry_sdk.isolation_scope() as sentry_scope:
186
+ (
187
+ transaction_name,
188
+ transaction_source,
189
+ ) = self._get_transaction_name_and_source(
190
+ self.transaction_style,
191
+ scope,
192
+ )
193
+ sentry_scope.set_transaction_name(
194
+ transaction_name,
195
+ source=transaction_source,
196
+ )
197
+
178
198
  with track_session(sentry_scope, session_mode="request"):
179
199
  sentry_scope.clear_breadcrumbs()
180
200
  sentry_scope._name = "asgi"
@@ -182,82 +202,47 @@ class SentryAsgiMiddleware:
182
202
  sentry_scope.add_event_processor(processor)
183
203
 
184
204
  ty = scope["type"]
185
- (
186
- transaction_name,
187
- transaction_source,
188
- ) = self._get_transaction_name_and_source(
189
- self.transaction_style,
190
- scope,
191
- )
192
205
 
193
206
  method = scope.get("method", "").upper()
194
- transaction = None
195
- if ty in ("http", "websocket"):
196
- if ty == "websocket" or method in self.http_methods_to_capture:
197
- transaction = continue_trace(
198
- _get_headers(scope),
199
- op="{}.server".format(ty),
200
- name=transaction_name,
201
- source=transaction_source,
202
- origin=self.span_origin,
203
- )
204
- logger.debug(
205
- "[ASGI] Created transaction (continuing trace): %s",
206
- transaction,
207
- )
208
- else:
209
- transaction = Transaction(
210
- op=OP.HTTP_SERVER,
207
+ should_trace = ty == "websocket" or (
208
+ ty == "http" and method in self.http_methods_to_capture
209
+ )
210
+ if not should_trace:
211
+ return await self._run_original_app(
212
+ scope, receive, send, asgi_version
213
+ )
214
+
215
+ with sentry_sdk.continue_trace(_get_headers(scope)):
216
+ with sentry_sdk.start_span(
217
+ op=(
218
+ OP.WEBSOCKET_SERVER
219
+ if ty == "websocket"
220
+ else OP.HTTP_SERVER
221
+ ),
211
222
  name=transaction_name,
212
223
  source=transaction_source,
213
224
  origin=self.span_origin,
214
- )
215
- logger.debug(
216
- "[ASGI] Created transaction (new): %s", transaction
217
- )
218
-
219
- if transaction:
220
- transaction.set_tag("asgi.type", ty)
221
- logger.debug(
222
- "[ASGI] Set transaction name and source on transaction: '%s' / '%s'",
223
- transaction.name,
224
- transaction.source,
225
- )
226
-
227
- with (
228
- sentry_sdk.start_transaction(
229
- transaction,
230
- custom_sampling_context={"asgi_scope": scope},
231
- )
232
- if transaction is not None
233
- else nullcontext()
234
- ):
235
- logger.debug("[ASGI] Started transaction: %s", transaction)
236
- try:
225
+ attributes=_prepopulate_attributes(scope),
226
+ ) as span:
227
+ if span is not None:
228
+ logger.debug("[ASGI] Started transaction: %s", span)
229
+ span.set_tag("asgi.type", ty)
237
230
 
238
231
  async def _sentry_wrapped_send(event):
239
232
  # type: (Dict[str, Any]) -> Any
240
- if transaction is not None:
241
- is_http_response = (
242
- event.get("type") == "http.response.start"
243
- and "status" in event
244
- )
245
- if is_http_response:
246
- transaction.set_http_status(event["status"])
233
+ is_http_response = (
234
+ event.get("type") == "http.response.start"
235
+ and span is not None
236
+ and "status" in event
237
+ )
238
+ if is_http_response:
239
+ span.set_http_status(event["status"])
247
240
 
248
241
  return await send(event)
249
242
 
250
- if asgi_version == 2:
251
- return await self.app(scope)(
252
- receive, _sentry_wrapped_send
253
- )
254
- else:
255
- return await self.app(
256
- scope, receive, _sentry_wrapped_send
257
- )
258
- except Exception as exc:
259
- _capture_exception(exc, mechanism_type=self.mechanism_type)
260
- raise exc from None
243
+ return await self._run_original_app(
244
+ scope, receive, _sentry_wrapped_send, asgi_version
245
+ )
261
246
  finally:
262
247
  _asgi_middleware_applied.set(False)
263
248
 
@@ -336,3 +321,37 @@ class SentryAsgiMiddleware:
336
321
  return name, source
337
322
 
338
323
  return name, source
324
+
325
+
326
+ def _prepopulate_attributes(scope):
327
+ # type: (Any) -> dict[str, Any]
328
+ """Unpack ASGI scope into serializable OTel attributes."""
329
+ scope = scope or {}
330
+
331
+ attributes = {}
332
+ for attr, key in ASGI_SCOPE_PROPERTY_TO_ATTRIBUTE.items():
333
+ if scope.get(attr):
334
+ attributes[key] = scope[attr]
335
+
336
+ for attr in ("client", "server"):
337
+ if scope.get(attr):
338
+ try:
339
+ host, port = scope[attr]
340
+ attributes[f"{attr}.address"] = host
341
+ if port is not None:
342
+ attributes[f"{attr}.port"] = port
343
+ except Exception:
344
+ pass
345
+
346
+ with capture_internal_exceptions():
347
+ full_url = _get_url(scope)
348
+ query = _get_query(scope)
349
+ if query:
350
+ attributes["url.query"] = query
351
+ full_url = f"{full_url}?{query}"
352
+
353
+ attributes["url.full"] = full_url
354
+
355
+ attributes.update(_request_headers_to_span_attributes(_get_headers(scope)))
356
+
357
+ return attributes
@@ -48,6 +48,7 @@ def patch_asyncio():
48
48
  op=OP.FUNCTION,
49
49
  name=get_name(coro),
50
50
  origin=AsyncioIntegration.origin,
51
+ only_if_parent=True,
51
52
  ):
52
53
  try:
53
54
  result = await coro
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
  import contextlib
3
- from typing import Any, TypeVar, Callable, Awaitable, Iterator
3
+ from typing import Any, TypeVar, Callable, Awaitable, Iterator, Optional
4
4
 
5
5
  import sentry_sdk
6
6
  from sentry_sdk.consts import OP, SPANDATA
@@ -8,6 +8,7 @@ from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotE
8
8
  from sentry_sdk.tracing import Span
9
9
  from sentry_sdk.tracing_utils import add_query_source, record_sql_queries
10
10
  from sentry_sdk.utils import (
11
+ _serialize_span_attribute,
11
12
  ensure_integration_enabled,
12
13
  parse_version,
13
14
  capture_internal_exceptions,
@@ -38,7 +39,6 @@ class AsyncPGIntegration(Integration):
38
39
  asyncpg.Connection.execute = _wrap_execute(
39
40
  asyncpg.Connection.execute,
40
41
  )
41
-
42
42
  asyncpg.Connection._execute = _wrap_connection_method(
43
43
  asyncpg.Connection._execute
44
44
  )
@@ -78,8 +78,8 @@ def _wrap_execute(f: Callable[..., Awaitable[T]]) -> Callable[..., Awaitable[T]]
78
78
  ) as span:
79
79
  res = await f(*args, **kwargs)
80
80
 
81
- with capture_internal_exceptions():
82
- add_query_source(span)
81
+ with capture_internal_exceptions():
82
+ add_query_source(span)
83
83
 
84
84
  return res
85
85
 
@@ -121,10 +121,13 @@ def _wrap_connection_method(
121
121
  async def _inner(*args: Any, **kwargs: Any) -> T:
122
122
  if sentry_sdk.get_client().get_integration(AsyncPGIntegration) is None:
123
123
  return await f(*args, **kwargs)
124
+
124
125
  query = args[1]
125
126
  params_list = args[2] if len(args) > 2 else None
127
+
126
128
  with _record(None, query, params_list, executemany=executemany) as span:
127
- _set_db_data(span, args[0])
129
+ data = _get_db_data(conn=args[0])
130
+ _set_on_span(span, data)
128
131
  res = await f(*args, **kwargs)
129
132
 
130
133
  return res
@@ -144,9 +147,10 @@ def _wrap_cursor_creation(f: Callable[..., T]) -> Callable[..., T]:
144
147
  params_list,
145
148
  executemany=False,
146
149
  ) as span:
147
- _set_db_data(span, args[0])
150
+ data = _get_db_data(conn=args[0])
151
+ _set_on_span(span, data)
148
152
  res = f(*args, **kwargs)
149
- span.set_data("db.cursor", res)
153
+ span.set_attribute("db.cursor", _serialize_span_attribute(res))
150
154
 
151
155
  return res
152
156
 
@@ -158,29 +162,24 @@ def _wrap_connect_addr(f: Callable[..., Awaitable[T]]) -> Callable[..., Awaitabl
158
162
  if sentry_sdk.get_client().get_integration(AsyncPGIntegration) is None:
159
163
  return await f(*args, **kwargs)
160
164
 
161
- user = kwargs["params"].user
162
- database = kwargs["params"].database
163
-
164
165
  with sentry_sdk.start_span(
165
166
  op=OP.DB,
166
167
  name="connect",
167
168
  origin=AsyncPGIntegration.origin,
169
+ only_if_parent=True,
168
170
  ) as span:
169
- span.set_data(SPANDATA.DB_SYSTEM, "postgresql")
170
- addr = kwargs.get("addr")
171
- if addr:
172
- try:
173
- span.set_data(SPANDATA.SERVER_ADDRESS, addr[0])
174
- span.set_data(SPANDATA.SERVER_PORT, addr[1])
175
- except IndexError:
176
- pass
177
- span.set_data(SPANDATA.DB_NAME, database)
178
- span.set_data(SPANDATA.DB_USER, user)
171
+ data = _get_db_data(
172
+ addr=kwargs.get("addr"),
173
+ database=kwargs["params"].database,
174
+ user=kwargs["params"].user,
175
+ )
176
+ _set_on_span(span, data)
179
177
 
180
178
  with capture_internal_exceptions():
181
179
  sentry_sdk.add_breadcrumb(
182
- message="connect", category="query", data=span._data
180
+ message="connect", category="query", data=data
183
181
  )
182
+
184
183
  res = await f(*args, **kwargs)
185
184
 
186
185
  return res
@@ -188,21 +187,37 @@ def _wrap_connect_addr(f: Callable[..., Awaitable[T]]) -> Callable[..., Awaitabl
188
187
  return _inner
189
188
 
190
189
 
191
- def _set_db_data(span: Span, conn: Any) -> None:
192
- span.set_data(SPANDATA.DB_SYSTEM, "postgresql")
190
+ def _get_db_data(
191
+ conn: Any = None,
192
+ addr: Optional[tuple[str, ...]] = None,
193
+ database: Optional[str] = None,
194
+ user: Optional[str] = None,
195
+ ) -> dict[str, str]:
196
+ if conn is not None:
197
+ addr = conn._addr
198
+ database = conn._params.database
199
+ user = conn._params.user
200
+
201
+ data = {
202
+ SPANDATA.DB_SYSTEM: "postgresql",
203
+ }
193
204
 
194
- addr = conn._addr
195
205
  if addr:
196
206
  try:
197
- span.set_data(SPANDATA.SERVER_ADDRESS, addr[0])
198
- span.set_data(SPANDATA.SERVER_PORT, addr[1])
207
+ data[SPANDATA.SERVER_ADDRESS] = addr[0]
208
+ data[SPANDATA.SERVER_PORT] = addr[1]
199
209
  except IndexError:
200
210
  pass
201
211
 
202
- database = conn._params.database
203
212
  if database:
204
- span.set_data(SPANDATA.DB_NAME, database)
213
+ data[SPANDATA.DB_NAME] = database
205
214
 
206
- user = conn._params.user
207
215
  if user:
208
- span.set_data(SPANDATA.DB_USER, user)
216
+ data[SPANDATA.DB_USER] = user
217
+
218
+ return data
219
+
220
+
221
+ def _set_on_span(span: Span, data: dict[str, Any]) -> None:
222
+ for key, value in data.items():
223
+ span.set_attribute(key, value)