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
@@ -5,7 +5,7 @@ https://github.com/getsentry/rb
5
5
  """
6
6
 
7
7
  from sentry_sdk.integrations.redis._sync_common import patch_redis_client
8
- from sentry_sdk.integrations.redis.modules.queries import _set_db_data
8
+ from sentry_sdk.integrations.redis.modules.queries import _get_db_data
9
9
 
10
10
 
11
11
  def _patch_rb():
@@ -18,15 +18,15 @@ def _patch_rb():
18
18
  patch_redis_client(
19
19
  rb.clients.FanoutClient,
20
20
  is_cluster=False,
21
- set_db_data_fn=_set_db_data,
21
+ get_db_data_fn=_get_db_data,
22
22
  )
23
23
  patch_redis_client(
24
24
  rb.clients.MappingClient,
25
25
  is_cluster=False,
26
- set_db_data_fn=_set_db_data,
26
+ get_db_data_fn=_get_db_data,
27
27
  )
28
28
  patch_redis_client(
29
29
  rb.clients.RoutingClient,
30
30
  is_cluster=False,
31
- set_db_data_fn=_set_db_data,
31
+ get_db_data_fn=_get_db_data,
32
32
  )
@@ -8,7 +8,7 @@ from sentry_sdk.integrations.redis._sync_common import (
8
8
  patch_redis_client,
9
9
  patch_redis_pipeline,
10
10
  )
11
- from sentry_sdk.integrations.redis.modules.queries import _set_db_data
11
+ from sentry_sdk.integrations.redis.modules.queries import _get_db_data
12
12
 
13
13
  from typing import TYPE_CHECKING
14
14
 
@@ -26,13 +26,13 @@ def _patch_redis(StrictRedis, client): # noqa: N803
26
26
  patch_redis_client(
27
27
  StrictRedis,
28
28
  is_cluster=False,
29
- set_db_data_fn=_set_db_data,
29
+ get_db_data_fn=_get_db_data,
30
30
  )
31
31
  patch_redis_pipeline(
32
32
  client.Pipeline,
33
33
  is_cluster=False,
34
34
  get_command_args_fn=_get_redis_command_args,
35
- set_db_data_fn=_set_db_data,
35
+ get_db_data_fn=_get_db_data,
36
36
  )
37
37
  try:
38
38
  strict_pipeline = client.StrictPipeline
@@ -43,7 +43,7 @@ def _patch_redis(StrictRedis, client): # noqa: N803
43
43
  strict_pipeline,
44
44
  is_cluster=False,
45
45
  get_command_args_fn=_get_redis_command_args,
46
- set_db_data_fn=_set_db_data,
46
+ get_db_data_fn=_get_db_data,
47
47
  )
48
48
 
49
49
  try:
@@ -59,11 +59,11 @@ def _patch_redis(StrictRedis, client): # noqa: N803
59
59
  patch_redis_async_client(
60
60
  redis.asyncio.client.StrictRedis,
61
61
  is_cluster=False,
62
- set_db_data_fn=_set_db_data,
62
+ get_db_data_fn=_get_db_data,
63
63
  )
64
64
  patch_redis_async_pipeline(
65
65
  redis.asyncio.client.Pipeline,
66
66
  False,
67
67
  _get_redis_command_args,
68
- set_db_data_fn=_set_db_data,
68
+ get_db_data_fn=_get_db_data,
69
69
  )
@@ -9,7 +9,7 @@ from sentry_sdk.integrations.redis._sync_common import (
9
9
  patch_redis_client,
10
10
  patch_redis_pipeline,
11
11
  )
12
- from sentry_sdk.integrations.redis.modules.queries import _set_db_data_on_span
12
+ from sentry_sdk.integrations.redis.modules.queries import _get_connection_data
13
13
  from sentry_sdk.integrations.redis.utils import _parse_rediscluster_command
14
14
 
15
15
  from sentry_sdk.utils import capture_internal_exceptions
@@ -23,29 +23,29 @@ if TYPE_CHECKING:
23
23
  RedisCluster as AsyncRedisCluster,
24
24
  ClusterPipeline as AsyncClusterPipeline,
25
25
  )
26
- from sentry_sdk.tracing import Span
27
26
 
28
27
 
29
- def _set_async_cluster_db_data(span, async_redis_cluster_instance):
30
- # type: (Span, AsyncRedisCluster[Any]) -> None
28
+ def _get_async_cluster_db_data(async_redis_cluster_instance):
29
+ # type: (AsyncRedisCluster[Any]) -> dict[str, Any]
31
30
  default_node = async_redis_cluster_instance.get_default_node()
32
31
  if default_node is not None and default_node.connection_kwargs is not None:
33
- _set_db_data_on_span(span, default_node.connection_kwargs)
32
+ return _get_connection_data(default_node.connection_kwargs)
33
+ else:
34
+ return {}
34
35
 
35
36
 
36
- def _set_async_cluster_pipeline_db_data(span, async_redis_cluster_pipeline_instance):
37
- # type: (Span, AsyncClusterPipeline[Any]) -> None
37
+ def _get_async_cluster_pipeline_db_data(async_redis_cluster_pipeline_instance):
38
+ # type: (AsyncClusterPipeline[Any]) -> dict[str, Any]
38
39
  with capture_internal_exceptions():
39
- _set_async_cluster_db_data(
40
- span,
40
+ return _get_async_cluster_db_data(
41
41
  # the AsyncClusterPipeline has always had a `_client` attr but it is private so potentially problematic and mypy
42
42
  # does not recognize it - see https://github.com/redis/redis-py/blame/v5.0.0/redis/asyncio/cluster.py#L1386
43
43
  async_redis_cluster_pipeline_instance._client, # type: ignore[attr-defined]
44
44
  )
45
45
 
46
46
 
47
- def _set_cluster_db_data(span, redis_cluster_instance):
48
- # type: (Span, RedisCluster[Any]) -> None
47
+ def _get_cluster_db_data(redis_cluster_instance):
48
+ # type: (RedisCluster[Any]) -> dict[str, Any]
49
49
  default_node = redis_cluster_instance.get_default_node()
50
50
 
51
51
  if default_node is not None:
@@ -53,7 +53,9 @@ def _set_cluster_db_data(span, redis_cluster_instance):
53
53
  "host": default_node.host,
54
54
  "port": default_node.port,
55
55
  }
56
- _set_db_data_on_span(span, connection_params)
56
+ return _get_connection_data(connection_params)
57
+ else:
58
+ return {}
57
59
 
58
60
 
59
61
  def _patch_redis_cluster():
@@ -67,13 +69,13 @@ def _patch_redis_cluster():
67
69
  patch_redis_client(
68
70
  RedisCluster,
69
71
  is_cluster=True,
70
- set_db_data_fn=_set_cluster_db_data,
72
+ get_db_data_fn=_get_cluster_db_data,
71
73
  )
72
74
  patch_redis_pipeline(
73
75
  cluster.ClusterPipeline,
74
76
  is_cluster=True,
75
77
  get_command_args_fn=_parse_rediscluster_command,
76
- set_db_data_fn=_set_cluster_db_data,
78
+ get_db_data_fn=_get_cluster_db_data,
77
79
  )
78
80
 
79
81
  try:
@@ -89,11 +91,11 @@ def _patch_redis_cluster():
89
91
  patch_redis_async_client(
90
92
  async_cluster.RedisCluster,
91
93
  is_cluster=True,
92
- set_db_data_fn=_set_async_cluster_db_data,
94
+ get_db_data_fn=_get_async_cluster_db_data,
93
95
  )
94
96
  patch_redis_async_pipeline(
95
97
  async_cluster.ClusterPipeline,
96
98
  is_cluster=True,
97
99
  get_command_args_fn=_parse_rediscluster_command,
98
- set_db_data_fn=_set_async_cluster_pipeline_db_data,
100
+ get_db_data_fn=_get_async_cluster_pipeline_db_data,
99
101
  )
@@ -9,7 +9,7 @@ from sentry_sdk.integrations.redis._sync_common import (
9
9
  patch_redis_client,
10
10
  patch_redis_pipeline,
11
11
  )
12
- from sentry_sdk.integrations.redis.modules.queries import _set_db_data
12
+ from sentry_sdk.integrations.redis.modules.queries import _get_db_data
13
13
  from sentry_sdk.integrations.redis.utils import _parse_rediscluster_command
14
14
 
15
15
 
@@ -23,7 +23,7 @@ def _patch_rediscluster():
23
23
  patch_redis_client(
24
24
  rediscluster.RedisCluster,
25
25
  is_cluster=True,
26
- set_db_data_fn=_set_db_data,
26
+ get_db_data_fn=_get_db_data,
27
27
  )
28
28
 
29
29
  # up to v1.3.6, __version__ attribute is a tuple
@@ -37,7 +37,7 @@ def _patch_rediscluster():
37
37
  patch_redis_client(
38
38
  rediscluster.StrictRedisCluster,
39
39
  is_cluster=True,
40
- set_db_data_fn=_set_db_data,
40
+ get_db_data_fn=_get_db_data,
41
41
  )
42
42
  else:
43
43
  pipeline_cls = rediscluster.pipeline.ClusterPipeline
@@ -46,5 +46,5 @@ def _patch_rediscluster():
46
46
  pipeline_cls,
47
47
  is_cluster=True,
48
48
  get_command_args_fn=_parse_rediscluster_command,
49
- set_db_data_fn=_set_db_data,
49
+ get_db_data_fn=_get_db_data,
50
50
  )
@@ -1,3 +1,4 @@
1
+ import sentry_sdk
1
2
  from sentry_sdk.consts import SPANDATA
2
3
  from sentry_sdk.integrations.redis.consts import (
3
4
  _COMMANDS_INCLUDING_SENSITIVE_DATA,
@@ -16,6 +17,47 @@ if TYPE_CHECKING:
16
17
  from sentry_sdk.tracing import Span
17
18
 
18
19
 
20
+ TAG_KEYS = [
21
+ "redis.command",
22
+ "redis.is_cluster",
23
+ "redis.key",
24
+ "redis.transaction",
25
+ SPANDATA.DB_OPERATION,
26
+ ]
27
+
28
+
29
+ def _update_span(span, *data_bags):
30
+ # type: (Span, *dict[str, Any]) -> None
31
+ """
32
+ Set tags and data on the given span to data from the given data bags.
33
+ """
34
+ for data in data_bags:
35
+ for key, value in data.items():
36
+ if key in TAG_KEYS:
37
+ span.set_tag(key, value)
38
+ else:
39
+ span.set_attribute(key, value)
40
+
41
+
42
+ def _create_breadcrumb(message, *data_bags):
43
+ # type: (str, *dict[str, Any]) -> None
44
+ """
45
+ Create a breadcrumb containing the tags data from the given data bags.
46
+ """
47
+ data = {}
48
+ for data in data_bags:
49
+ for key, value in data.items():
50
+ if key in TAG_KEYS:
51
+ data[key] = value
52
+
53
+ sentry_sdk.add_breadcrumb(
54
+ message=message,
55
+ type="redis",
56
+ category="redis",
57
+ data=data,
58
+ )
59
+
60
+
19
61
  def _get_safe_command(name, args):
20
62
  # type: (str, Sequence[Any]) -> str
21
63
  command_parts = [name]
@@ -105,12 +147,12 @@ def _parse_rediscluster_command(command):
105
147
  return command.args
106
148
 
107
149
 
108
- def _set_pipeline_data(
109
- span, is_cluster, get_command_args_fn, is_transaction, command_stack
110
- ):
111
- # type: (Span, bool, Any, bool, Sequence[Any]) -> None
112
- span.set_tag("redis.is_cluster", is_cluster)
113
- span.set_tag("redis.transaction", is_transaction)
150
+ def _get_pipeline_data(is_cluster, get_command_args_fn, is_transaction, command_stack):
151
+ # type: (bool, Any, bool, Sequence[Any]) -> dict[str, Any]
152
+ data = {
153
+ "redis.is_cluster": is_cluster,
154
+ "redis.transaction": is_transaction,
155
+ } # type: dict[str, Any]
114
156
 
115
157
  commands = []
116
158
  for i, arg in enumerate(command_stack):
@@ -120,25 +162,27 @@ def _set_pipeline_data(
120
162
  command = get_command_args_fn(arg)
121
163
  commands.append(_get_safe_command(command[0], command[1:]))
122
164
 
123
- span.set_data(
124
- "redis.commands",
125
- {
126
- "count": len(command_stack),
127
- "first_ten": commands,
128
- },
129
- )
165
+ data["redis.commands.count"] = len(command_stack)
166
+ data["redis.commands.first_ten"] = commands
167
+
168
+ return data
130
169
 
131
170
 
132
- def _set_client_data(span, is_cluster, name, *args):
133
- # type: (Span, bool, str, *Any) -> None
134
- span.set_tag("redis.is_cluster", is_cluster)
171
+ def _get_client_data(is_cluster, name, *args):
172
+ # type: (bool, str, *Any) -> dict[str, Any]
173
+ data = {
174
+ "redis.is_cluster": is_cluster,
175
+ } # type: dict[str, Any]
176
+
135
177
  if name:
136
- span.set_tag("redis.command", name)
137
- span.set_tag(SPANDATA.DB_OPERATION, name)
178
+ data["redis.command"] = name
179
+ data[SPANDATA.DB_OPERATION] = name
138
180
 
139
181
  if name and args:
140
182
  name_low = name.lower()
141
183
  if (name_low in _SINGLE_KEY_COMMANDS) or (
142
184
  name_low in _MULTI_KEY_COMMANDS and len(args) == 1
143
185
  ):
144
- span.set_tag("redis.key", args[0])
186
+ data["redis.key"] = args[0]
187
+
188
+ return data
@@ -2,7 +2,6 @@ import weakref
2
2
 
3
3
  import sentry_sdk
4
4
  from sentry_sdk.consts import OP
5
- from sentry_sdk.api import continue_trace
6
5
  from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
7
6
  from sentry_sdk.integrations.logging import ignore_logger
8
7
  from sentry_sdk.tracing import TransactionSource
@@ -33,6 +32,17 @@ if TYPE_CHECKING:
33
32
 
34
33
  from rq.job import Job
35
34
 
35
+ DEFAULT_TRANSACTION_NAME = "unknown RQ task"
36
+
37
+
38
+ JOB_PROPERTY_TO_ATTRIBUTE = {
39
+ "id": "messaging.message.id",
40
+ }
41
+
42
+ QUEUE_PROPERTY_TO_ATTRIBUTE = {
43
+ "name": "messaging.destination.name",
44
+ }
45
+
36
46
 
37
47
  class RqIntegration(Integration):
38
48
  identifier = "rq"
@@ -47,28 +57,31 @@ class RqIntegration(Integration):
47
57
  old_perform_job = Worker.perform_job
48
58
 
49
59
  @ensure_integration_enabled(RqIntegration, old_perform_job)
50
- def sentry_patched_perform_job(self, job, *args, **kwargs):
51
- # type: (Any, Job, *Queue, **Any) -> bool
60
+ def sentry_patched_perform_job(self, job, queue, *args, **kwargs):
61
+ # type: (Any, Job, Queue, *Any, **Any) -> bool
52
62
  with sentry_sdk.new_scope() as scope:
53
- scope.clear_breadcrumbs()
54
- scope.add_event_processor(_make_event_processor(weakref.ref(job)))
63
+ try:
64
+ transaction_name = job.func_name or DEFAULT_TRANSACTION_NAME
65
+ except AttributeError:
66
+ transaction_name = DEFAULT_TRANSACTION_NAME
55
67
 
56
- transaction = continue_trace(
57
- job.meta.get("_sentry_trace_headers") or {},
58
- op=OP.QUEUE_TASK_RQ,
59
- name="unknown RQ task",
60
- source=TransactionSource.TASK,
61
- origin=RqIntegration.origin,
68
+ scope.set_transaction_name(
69
+ transaction_name, source=TransactionSource.TASK
62
70
  )
71
+ scope.clear_breadcrumbs()
72
+ scope.add_event_processor(_make_event_processor(weakref.ref(job)))
63
73
 
64
- with capture_internal_exceptions():
65
- transaction.name = job.func_name
66
-
67
- with sentry_sdk.start_transaction(
68
- transaction,
69
- custom_sampling_context={"rq_job": job},
74
+ with sentry_sdk.continue_trace(
75
+ job.meta.get("_sentry_trace_headers") or {}
70
76
  ):
71
- rv = old_perform_job(self, job, *args, **kwargs)
77
+ with sentry_sdk.start_span(
78
+ op=OP.QUEUE_TASK_RQ,
79
+ name=transaction_name,
80
+ source=TransactionSource.TASK,
81
+ origin=RqIntegration.origin,
82
+ attributes=_prepopulate_attributes(job, queue),
83
+ ):
84
+ rv = old_perform_job(self, job, queue, *args, **kwargs)
72
85
 
73
86
  if self.is_horse:
74
87
  # We're inside of a forked process and RQ is
@@ -102,11 +115,9 @@ class RqIntegration(Integration):
102
115
  @ensure_integration_enabled(RqIntegration, old_enqueue_job)
103
116
  def sentry_patched_enqueue_job(self, job, **kwargs):
104
117
  # type: (Queue, Any, **Any) -> Any
105
- scope = sentry_sdk.get_current_scope()
106
- if scope.span is not None:
107
- job.meta["_sentry_trace_headers"] = dict(
108
- scope.iter_trace_propagation_headers()
109
- )
118
+ job.meta["_sentry_trace_headers"] = dict(
119
+ sentry_sdk.get_current_scope().iter_trace_propagation_headers()
120
+ )
110
121
 
111
122
  return old_enqueue_job(self, job, **kwargs)
112
123
 
@@ -159,3 +170,37 @@ def _capture_exception(exc_info, **kwargs):
159
170
  )
160
171
 
161
172
  sentry_sdk.capture_event(event, hint=hint)
173
+
174
+
175
+ def _prepopulate_attributes(job, queue):
176
+ # type: (Job, Queue) -> dict[str, Any]
177
+ attributes = {
178
+ "messaging.system": "rq",
179
+ "rq.job.id": job.id,
180
+ }
181
+
182
+ for prop, attr in JOB_PROPERTY_TO_ATTRIBUTE.items():
183
+ if getattr(job, prop, None) is not None:
184
+ attributes[attr] = getattr(job, prop)
185
+
186
+ for prop, attr in QUEUE_PROPERTY_TO_ATTRIBUTE.items():
187
+ if getattr(queue, prop, None) is not None:
188
+ attributes[attr] = getattr(queue, prop)
189
+
190
+ if getattr(job, "args", None):
191
+ for i, arg in enumerate(job.args):
192
+ with capture_internal_exceptions():
193
+ attributes[f"rq.job.args.{i}"] = str(arg)
194
+
195
+ if getattr(job, "kwargs", None):
196
+ for kwarg, value in job.kwargs.items():
197
+ with capture_internal_exceptions():
198
+ attributes[f"rq.job.kwargs.{kwarg}"] = str(value)
199
+
200
+ func = job.func
201
+ if callable(func):
202
+ func = func.__name__
203
+
204
+ attributes["rq.job.func"] = str(func)
205
+
206
+ return attributes
@@ -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