sentry-sdk 2.26.1__py2.py3-none-any.whl → 3.0.0a1__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of sentry-sdk might be problematic. Click here for more details.

Files changed (114) hide show
  1. sentry_sdk/__init__.py +4 -8
  2. sentry_sdk/_compat.py +0 -1
  3. sentry_sdk/_init_implementation.py +6 -44
  4. sentry_sdk/_log_batcher.py +47 -28
  5. sentry_sdk/_types.py +8 -64
  6. sentry_sdk/ai/monitoring.py +14 -10
  7. sentry_sdk/ai/utils.py +1 -1
  8. sentry_sdk/api.py +69 -163
  9. sentry_sdk/client.py +25 -72
  10. sentry_sdk/consts.py +42 -23
  11. sentry_sdk/debug.py +0 -10
  12. sentry_sdk/envelope.py +2 -10
  13. sentry_sdk/feature_flags.py +5 -1
  14. sentry_sdk/integrations/__init__.py +5 -2
  15. sentry_sdk/integrations/_asgi_common.py +3 -3
  16. sentry_sdk/integrations/_wsgi_common.py +11 -40
  17. sentry_sdk/integrations/aiohttp.py +104 -57
  18. sentry_sdk/integrations/anthropic.py +10 -7
  19. sentry_sdk/integrations/arq.py +24 -13
  20. sentry_sdk/integrations/asgi.py +103 -83
  21. sentry_sdk/integrations/asyncio.py +1 -0
  22. sentry_sdk/integrations/asyncpg.py +45 -30
  23. sentry_sdk/integrations/aws_lambda.py +109 -92
  24. sentry_sdk/integrations/boto3.py +38 -9
  25. sentry_sdk/integrations/bottle.py +1 -1
  26. sentry_sdk/integrations/celery/__init__.py +48 -38
  27. sentry_sdk/integrations/clickhouse_driver.py +59 -28
  28. sentry_sdk/integrations/cohere.py +2 -0
  29. sentry_sdk/integrations/django/__init__.py +25 -46
  30. sentry_sdk/integrations/django/asgi.py +6 -2
  31. sentry_sdk/integrations/django/caching.py +13 -22
  32. sentry_sdk/integrations/django/middleware.py +1 -0
  33. sentry_sdk/integrations/django/signals_handlers.py +3 -1
  34. sentry_sdk/integrations/django/templates.py +8 -12
  35. sentry_sdk/integrations/django/transactions.py +1 -6
  36. sentry_sdk/integrations/django/views.py +5 -2
  37. sentry_sdk/integrations/falcon.py +7 -25
  38. sentry_sdk/integrations/fastapi.py +3 -3
  39. sentry_sdk/integrations/flask.py +1 -1
  40. sentry_sdk/integrations/gcp.py +63 -38
  41. sentry_sdk/integrations/graphene.py +6 -13
  42. sentry_sdk/integrations/grpc/aio/client.py +14 -8
  43. sentry_sdk/integrations/grpc/aio/server.py +19 -21
  44. sentry_sdk/integrations/grpc/client.py +8 -6
  45. sentry_sdk/integrations/grpc/server.py +12 -14
  46. sentry_sdk/integrations/httpx.py +47 -12
  47. sentry_sdk/integrations/huey.py +26 -22
  48. sentry_sdk/integrations/huggingface_hub.py +1 -0
  49. sentry_sdk/integrations/langchain.py +22 -15
  50. sentry_sdk/integrations/launchdarkly.py +3 -3
  51. sentry_sdk/integrations/litestar.py +4 -2
  52. sentry_sdk/integrations/logging.py +12 -3
  53. sentry_sdk/integrations/openai.py +2 -0
  54. sentry_sdk/integrations/openfeature.py +3 -5
  55. sentry_sdk/integrations/pymongo.py +18 -25
  56. sentry_sdk/integrations/pyramid.py +1 -1
  57. sentry_sdk/integrations/quart.py +3 -3
  58. sentry_sdk/integrations/ray.py +23 -17
  59. sentry_sdk/integrations/redis/_async_common.py +30 -18
  60. sentry_sdk/integrations/redis/_sync_common.py +28 -18
  61. sentry_sdk/integrations/redis/modules/caches.py +13 -10
  62. sentry_sdk/integrations/redis/modules/queries.py +14 -11
  63. sentry_sdk/integrations/redis/rb.py +4 -4
  64. sentry_sdk/integrations/redis/redis.py +6 -6
  65. sentry_sdk/integrations/redis/redis_cluster.py +18 -16
  66. sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +4 -4
  67. sentry_sdk/integrations/redis/utils.py +63 -19
  68. sentry_sdk/integrations/rq.py +68 -23
  69. sentry_sdk/integrations/rust_tracing.py +28 -43
  70. sentry_sdk/integrations/sanic.py +23 -13
  71. sentry_sdk/integrations/socket.py +9 -5
  72. sentry_sdk/integrations/sqlalchemy.py +8 -8
  73. sentry_sdk/integrations/starlette.py +11 -31
  74. sentry_sdk/integrations/starlite.py +4 -2
  75. sentry_sdk/integrations/stdlib.py +56 -9
  76. sentry_sdk/integrations/strawberry.py +40 -59
  77. sentry_sdk/integrations/threading.py +10 -26
  78. sentry_sdk/integrations/tornado.py +57 -18
  79. sentry_sdk/integrations/trytond.py +4 -1
  80. sentry_sdk/integrations/unleash.py +2 -3
  81. sentry_sdk/integrations/wsgi.py +84 -38
  82. sentry_sdk/opentelemetry/__init__.py +9 -0
  83. sentry_sdk/opentelemetry/consts.py +33 -0
  84. sentry_sdk/opentelemetry/contextvars_context.py +73 -0
  85. sentry_sdk/{integrations/opentelemetry → opentelemetry}/propagator.py +19 -28
  86. sentry_sdk/opentelemetry/sampler.py +326 -0
  87. sentry_sdk/opentelemetry/scope.py +218 -0
  88. sentry_sdk/opentelemetry/span_processor.py +329 -0
  89. sentry_sdk/opentelemetry/tracing.py +35 -0
  90. sentry_sdk/opentelemetry/utils.py +476 -0
  91. sentry_sdk/profiler/__init__.py +0 -40
  92. sentry_sdk/profiler/continuous_profiler.py +1 -30
  93. sentry_sdk/profiler/transaction_profiler.py +5 -56
  94. sentry_sdk/scope.py +107 -351
  95. sentry_sdk/sessions.py +0 -87
  96. sentry_sdk/tracing.py +418 -1134
  97. sentry_sdk/tracing_utils.py +134 -169
  98. sentry_sdk/transport.py +4 -104
  99. sentry_sdk/types.py +26 -2
  100. sentry_sdk/utils.py +169 -152
  101. {sentry_sdk-2.26.1.dist-info → sentry_sdk-3.0.0a1.dist-info}/METADATA +3 -5
  102. sentry_sdk-3.0.0a1.dist-info/RECORD +154 -0
  103. {sentry_sdk-2.26.1.dist-info → sentry_sdk-3.0.0a1.dist-info}/WHEEL +1 -1
  104. sentry_sdk-3.0.0a1.dist-info/entry_points.txt +2 -0
  105. sentry_sdk/hub.py +0 -739
  106. sentry_sdk/integrations/opentelemetry/__init__.py +0 -7
  107. sentry_sdk/integrations/opentelemetry/consts.py +0 -5
  108. sentry_sdk/integrations/opentelemetry/integration.py +0 -58
  109. sentry_sdk/integrations/opentelemetry/span_processor.py +0 -391
  110. sentry_sdk/metrics.py +0 -965
  111. sentry_sdk-2.26.1.dist-info/RECORD +0 -152
  112. sentry_sdk-2.26.1.dist-info/entry_points.txt +0 -2
  113. {sentry_sdk-2.26.1.dist-info → sentry_sdk-3.0.0a1.dist-info}/licenses/LICENSE +0 -0
  114. {sentry_sdk-2.26.1.dist-info → sentry_sdk-3.0.0a1.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  from typing import TYPE_CHECKING
2
- import sentry_sdk
3
2
 
3
+ from sentry_sdk.feature_flags import add_feature_flag
4
4
  from sentry_sdk.integrations import DidNotEnable, Integration
5
5
 
6
6
  try:
@@ -53,8 +53,8 @@ class LaunchDarklyHook(Hook):
53
53
  def after_evaluation(self, series_context, data, detail):
54
54
  # type: (EvaluationSeriesContext, dict[Any, Any], EvaluationDetail) -> dict[Any, Any]
55
55
  if isinstance(detail.value, bool):
56
- flags = sentry_sdk.get_current_scope().flags
57
- flags.set(series_context.key, detail.value)
56
+ add_feature_flag(series_context.key, detail.value)
57
+
58
58
  return data
59
59
 
60
60
  def before_evaluation(self, series_context, data):
@@ -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)
@@ -24,7 +24,7 @@ if TYPE_CHECKING:
24
24
  from typing import Optional
25
25
 
26
26
  DEFAULT_LEVEL = logging.INFO
27
- DEFAULT_EVENT_LEVEL = logging.ERROR
27
+ DEFAULT_EVENT_LEVEL = None # None means no events are captured
28
28
  LOGGING_TO_EVENT_LEVEL = {
29
29
  logging.NOTSET: "notset",
30
30
  logging.DEBUG: "debug",
@@ -43,7 +43,12 @@ LOGGING_TO_EVENT_LEVEL = {
43
43
  # Note: Ignoring by logger name here is better than mucking with thread-locals.
44
44
  # We do not necessarily know whether thread-locals work 100% correctly in the user's environment.
45
45
  _IGNORED_LOGGERS = set(
46
- ["sentry_sdk.errors", "urllib3.connectionpool", "urllib3.connection"]
46
+ [
47
+ "sentry_sdk.errors",
48
+ "urllib3.connectionpool",
49
+ "urllib3.connection",
50
+ "opentelemetry.*",
51
+ ]
47
52
  )
48
53
 
49
54
 
@@ -355,6 +360,7 @@ class SentryLogsHandler(_BaseHandler):
355
360
  # type: (BaseClient, LogRecord) -> None
356
361
  scope = sentry_sdk.get_current_scope()
357
362
  otel_severity_number, otel_severity_text = _python_level_to_otel(record.levelno)
363
+ project_root = client.options["project_root"]
358
364
  attrs = {
359
365
  "sentry.origin": "auto.logger.log",
360
366
  } # type: dict[str, str | bool | float | int]
@@ -374,7 +380,10 @@ class SentryLogsHandler(_BaseHandler):
374
380
  if record.lineno:
375
381
  attrs["code.line.number"] = record.lineno
376
382
  if record.pathname:
377
- attrs["code.file.path"] = record.pathname
383
+ if project_root is not None and record.pathname.startswith(project_root):
384
+ attrs["code.file.path"] = record.pathname[len(project_root) + 1 :]
385
+ else:
386
+ attrs["code.file.path"] = record.pathname
378
387
  if record.funcName:
379
388
  attrs["code.function.name"] = record.funcName
380
389
 
@@ -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,6 +1,6 @@
1
1
  from typing import TYPE_CHECKING
2
- import sentry_sdk
3
2
 
3
+ from sentry_sdk.feature_flags import add_feature_flag
4
4
  from sentry_sdk.integrations import DidNotEnable, Integration
5
5
 
6
6
  try:
@@ -29,11 +29,9 @@ class OpenFeatureHook(Hook):
29
29
  def after(self, hook_context, details, hints):
30
30
  # type: (HookContext, FlagEvaluationDetails[bool], HookHints) -> None
31
31
  if isinstance(details.value, bool):
32
- flags = sentry_sdk.get_current_scope().flags
33
- flags.set(details.flag_key, details.value)
32
+ add_feature_flag(details.flag_key, details.value)
34
33
 
35
34
  def error(self, hook_context, exception, hints):
36
35
  # type: (HookContext, Exception, HookHints) -> None
37
36
  if isinstance(hook_context.default_value, bool):
38
- flags = sentry_sdk.get_current_scope().flags
39
- flags.set(hook_context.flag_key, hook_context.default_value)
37
+ add_feature_flag(hook_context.flag_key, hook_context.default_value)
@@ -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,24 +40,28 @@ 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():
44
- set_db_data_fn(span, self)
45
- _set_pipeline_data(
46
- span,
47
- is_cluster,
48
- get_command_args_fn,
49
- False if is_cluster else self.is_transaction,
50
- self._command_stack if is_cluster else self.command_stack,
46
+ span_data = get_db_data_fn(self)
47
+ pipeline_data = _get_pipeline_data(
48
+ is_cluster=is_cluster,
49
+ get_command_args_fn=get_command_args_fn,
50
+ is_transaction=False if is_cluster else self.is_transaction,
51
+ command_stack=(
52
+ self._command_stack if is_cluster else self.command_stack
53
+ ),
51
54
  )
55
+ _update_span(span, span_data, pipeline_data)
56
+ _create_breadcrumb("redis.pipeline.execute", span_data, pipeline_data)
52
57
 
53
58
  return await old_execute(self, *args, **kwargs)
54
59
 
55
60
  pipeline_cls.execute = _sentry_execute # type: ignore
56
61
 
57
62
 
58
- def patch_redis_async_client(cls, is_cluster, set_db_data_fn):
59
- # type: (Union[type[StrictRedis[Any]], type[RedisCluster[Any]]], bool, Callable[[Span, Any], None]) -> None
63
+ def patch_redis_async_client(cls, is_cluster, get_db_data_fn):
64
+ # type: (Union[type[StrictRedis[Any]], type[RedisCluster[Any]]], bool, Callable[[Any], dict[str, Any]]) -> None
60
65
  old_execute_command = cls.execute_command
61
66
 
62
67
  from sentry_sdk.integrations.redis import RedisIntegration
@@ -80,6 +85,7 @@ def patch_redis_async_client(cls, is_cluster, set_db_data_fn):
80
85
  op=cache_properties["op"],
81
86
  name=cache_properties["description"],
82
87
  origin=SPAN_ORIGIN,
88
+ only_if_parent=True,
83
89
  )
84
90
  cache_span.__enter__()
85
91
 
@@ -89,18 +95,24 @@ def patch_redis_async_client(cls, is_cluster, set_db_data_fn):
89
95
  op=db_properties["op"],
90
96
  name=db_properties["description"],
91
97
  origin=SPAN_ORIGIN,
98
+ only_if_parent=True,
92
99
  )
93
100
  db_span.__enter__()
94
101
 
95
- set_db_data_fn(db_span, self)
96
- _set_client_data(db_span, is_cluster, name, *args)
102
+ db_span_data = get_db_data_fn(self)
103
+ db_client_span_data = _get_client_data(is_cluster, name, *args)
104
+ _update_span(db_span, db_span_data, db_client_span_data)
105
+ _create_breadcrumb(
106
+ db_properties["description"], db_span_data, db_client_span_data
107
+ )
97
108
 
98
109
  value = await old_execute_command(self, name, *args, **kwargs)
99
110
 
100
111
  db_span.__exit__(None, None, None)
101
112
 
102
113
  if cache_span:
103
- _set_cache_data(cache_span, self, cache_properties, value)
114
+ cache_span_data = _get_cache_data(self, cache_properties, value)
115
+ _update_span(cache_span, cache_span_data)
104
116
  cache_span.__exit__(None, None, None)
105
117
 
106
118
  return value
@@ -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
@@ -24,9 +25,9 @@ def patch_redis_pipeline(
24
25
  pipeline_cls,
25
26
  is_cluster,
26
27
  get_command_args_fn,
27
- set_db_data_fn,
28
+ get_db_data_fn,
28
29
  ):
29
- # type: (Any, bool, Any, Callable[[Span, Any], None]) -> None
30
+ # type: (Any, bool, Any, Callable[[Any], dict[str, Any]]) -> None
30
31
  old_execute = pipeline_cls.execute
31
32
 
32
33
  from sentry_sdk.integrations.redis import RedisIntegration
@@ -40,24 +41,26 @@ def patch_redis_pipeline(
40
41
  op=OP.DB_REDIS,
41
42
  name="redis.pipeline.execute",
42
43
  origin=SPAN_ORIGIN,
44
+ only_if_parent=True,
43
45
  ) as span:
44
46
  with capture_internal_exceptions():
45
- set_db_data_fn(span, self)
46
- _set_pipeline_data(
47
- span,
48
- is_cluster,
49
- get_command_args_fn,
50
- False if is_cluster else self.transaction,
51
- self.command_stack,
47
+ span_data = get_db_data_fn(self)
48
+ pipeline_data = _get_pipeline_data(
49
+ is_cluster=is_cluster,
50
+ get_command_args_fn=get_command_args_fn,
51
+ is_transaction=False if is_cluster else self.transaction,
52
+ command_stack=self.command_stack,
52
53
  )
54
+ _update_span(span, span_data, pipeline_data)
55
+ _create_breadcrumb("redis.pipeline.execute", span_data, pipeline_data)
53
56
 
54
57
  return old_execute(self, *args, **kwargs)
55
58
 
56
59
  pipeline_cls.execute = sentry_patched_execute
57
60
 
58
61
 
59
- def patch_redis_client(cls, is_cluster, set_db_data_fn):
60
- # type: (Any, bool, Callable[[Span, Any], None]) -> None
62
+ def patch_redis_client(cls, is_cluster, get_db_data_fn):
63
+ # type: (Any, bool, Callable[[Any], dict[str, Any]]) -> None
61
64
  """
62
65
  This function can be used to instrument custom redis client classes or
63
66
  subclasses.
@@ -85,6 +88,7 @@ def patch_redis_client(cls, is_cluster, set_db_data_fn):
85
88
  op=cache_properties["op"],
86
89
  name=cache_properties["description"],
87
90
  origin=SPAN_ORIGIN,
91
+ only_if_parent=True,
88
92
  )
89
93
  cache_span.__enter__()
90
94
 
@@ -94,18 +98,24 @@ def patch_redis_client(cls, is_cluster, set_db_data_fn):
94
98
  op=db_properties["op"],
95
99
  name=db_properties["description"],
96
100
  origin=SPAN_ORIGIN,
101
+ only_if_parent=True,
97
102
  )
98
103
  db_span.__enter__()
99
104
 
100
- set_db_data_fn(db_span, self)
101
- _set_client_data(db_span, is_cluster, name, *args)
105
+ db_span_data = get_db_data_fn(self)
106
+ db_client_span_data = _get_client_data(is_cluster, name, *args)
107
+ _update_span(db_span, db_span_data, db_client_span_data)
108
+ _create_breadcrumb(
109
+ db_properties["description"], db_span_data, db_client_span_data
110
+ )
102
111
 
103
112
  value = old_execute_command(self, name, *args, **kwargs)
104
113
 
105
114
  db_span.__exit__(None, None, None)
106
115
 
107
116
  if cache_span:
108
- _set_cache_data(cache_span, self, cache_properties, value)
117
+ cache_span_data = _get_cache_data(self, cache_properties, value)
118
+ _update_span(cache_span, cache_span_data)
109
119
  cache_span.__exit__(None, None, None)
110
120
 
111
121
  return value
@@ -13,7 +13,6 @@ from typing import TYPE_CHECKING
13
13
 
14
14
  if TYPE_CHECKING:
15
15
  from sentry_sdk.integrations.redis import RedisIntegration
16
- from sentry_sdk.tracing import Span
17
16
  from typing import Any, Optional
18
17
 
19
18
 
@@ -75,22 +74,24 @@ def _get_cache_span_description(redis_command, args, kwargs, integration):
75
74
  return description
76
75
 
77
76
 
78
- def _set_cache_data(span, redis_client, properties, return_value):
79
- # type: (Span, Any, dict[str, Any], Optional[Any]) -> None
77
+ def _get_cache_data(redis_client, properties, return_value):
78
+ # type: (Any, dict[str, Any], Optional[Any]) -> dict[str, Any]
79
+ data = {}
80
+
80
81
  with capture_internal_exceptions():
81
- span.set_data(SPANDATA.CACHE_KEY, properties["key"])
82
+ data[SPANDATA.CACHE_KEY] = properties["key"]
82
83
 
83
84
  if properties["redis_command"] in GET_COMMANDS:
84
85
  if return_value is not None:
85
- span.set_data(SPANDATA.CACHE_HIT, True)
86
+ data[SPANDATA.CACHE_HIT] = True
86
87
  size = (
87
88
  len(str(return_value).encode("utf-8"))
88
89
  if not isinstance(return_value, bytes)
89
90
  else len(return_value)
90
91
  )
91
- span.set_data(SPANDATA.CACHE_ITEM_SIZE, size)
92
+ data[SPANDATA.CACHE_ITEM_SIZE] = size
92
93
  else:
93
- span.set_data(SPANDATA.CACHE_HIT, False)
94
+ data[SPANDATA.CACHE_HIT] = False
94
95
 
95
96
  elif properties["redis_command"] in SET_COMMANDS:
96
97
  if properties["value"] is not None:
@@ -99,7 +100,7 @@ def _set_cache_data(span, redis_client, properties, return_value):
99
100
  if not isinstance(properties["value"], bytes)
100
101
  else len(properties["value"])
101
102
  )
102
- span.set_data(SPANDATA.CACHE_ITEM_SIZE, size)
103
+ data[SPANDATA.CACHE_ITEM_SIZE] = size
103
104
 
104
105
  try:
105
106
  connection_params = redis_client.connection_pool.connection_kwargs
@@ -114,8 +115,10 @@ def _set_cache_data(span, redis_client, properties, return_value):
114
115
 
115
116
  host = connection_params.get("host")
116
117
  if host is not None:
117
- span.set_data(SPANDATA.NETWORK_PEER_ADDRESS, host)
118
+ data[SPANDATA.NETWORK_PEER_ADDRESS] = host
118
119
 
119
120
  port = connection_params.get("port")
120
121
  if port is not None:
121
- span.set_data(SPANDATA.NETWORK_PEER_PORT, port)
122
+ data[SPANDATA.NETWORK_PEER_PORT] = port
123
+
124
+ return data
@@ -11,7 +11,6 @@ from typing import TYPE_CHECKING
11
11
  if TYPE_CHECKING:
12
12
  from redis import Redis
13
13
  from sentry_sdk.integrations.redis import RedisIntegration
14
- from sentry_sdk.tracing import Span
15
14
  from typing import Any
16
15
 
17
16
 
@@ -43,26 +42,30 @@ def _get_db_span_description(integration, command_name, args):
43
42
  return description
44
43
 
45
44
 
46
- def _set_db_data_on_span(span, connection_params):
47
- # type: (Span, dict[str, Any]) -> None
48
- span.set_data(SPANDATA.DB_SYSTEM, "redis")
45
+ def _get_connection_data(connection_params):
46
+ # type: (dict[str, Any]) -> dict[str, Any]
47
+ data = {
48
+ SPANDATA.DB_SYSTEM: "redis",
49
+ }
49
50
 
50
51
  db = connection_params.get("db")
51
52
  if db is not None:
52
- span.set_data(SPANDATA.DB_NAME, str(db))
53
+ data[SPANDATA.DB_NAME] = str(db)
53
54
 
54
55
  host = connection_params.get("host")
55
56
  if host is not None:
56
- span.set_data(SPANDATA.SERVER_ADDRESS, host)
57
+ data[SPANDATA.SERVER_ADDRESS] = host
57
58
 
58
59
  port = connection_params.get("port")
59
60
  if port is not None:
60
- span.set_data(SPANDATA.SERVER_PORT, port)
61
+ data[SPANDATA.SERVER_PORT] = port
62
+
63
+ return data
61
64
 
62
65
 
63
- def _set_db_data(span, redis_instance):
64
- # type: (Span, Redis[Any]) -> None
66
+ def _get_db_data(redis_instance):
67
+ # type: (Redis[Any]) -> dict[str, Any]
65
68
  try:
66
- _set_db_data_on_span(span, redis_instance.connection_pool.connection_kwargs)
69
+ return _get_connection_data(redis_instance.connection_pool.connection_kwargs)
67
70
  except AttributeError:
68
- pass # connections_kwargs may be missing in some cases
71
+ return {} # connections_kwargs may be missing in some cases