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
@@ -4,8 +4,7 @@ from functools import wraps
4
4
 
5
5
  import sentry_sdk
6
6
  from sentry_sdk import isolation_scope
7
- from sentry_sdk.api import continue_trace
8
- from sentry_sdk.consts import OP, SPANSTATUS, SPANDATA
7
+ from sentry_sdk.consts import OP, SPANSTATUS, SPANDATA, BAGGAGE_HEADER_NAME
9
8
  from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
10
9
  from sentry_sdk.integrations.celery.beat import (
11
10
  _patch_beat_apply_entry,
@@ -14,7 +13,7 @@ from sentry_sdk.integrations.celery.beat import (
14
13
  )
15
14
  from sentry_sdk.integrations.celery.utils import _now_seconds_since_epoch
16
15
  from sentry_sdk.integrations.logging import ignore_logger
17
- from sentry_sdk.tracing import BAGGAGE_HEADER_NAME, TransactionSource
16
+ from sentry_sdk.tracing import TransactionSource
18
17
  from sentry_sdk.tracing_utils import Baggage
19
18
  from sentry_sdk.utils import (
20
19
  capture_internal_exceptions,
@@ -101,9 +100,9 @@ class CeleryIntegration(Integration):
101
100
  def _set_status(status):
102
101
  # type: (str) -> None
103
102
  with capture_internal_exceptions():
104
- scope = sentry_sdk.get_current_scope()
105
- if scope.span is not None:
106
- scope.span.set_status(status)
103
+ span = sentry_sdk.get_current_span()
104
+ if span is not None:
105
+ span.set_status(status)
107
106
 
108
107
 
109
108
  def _capture_exception(task, exc_info):
@@ -113,7 +112,6 @@ def _capture_exception(task, exc_info):
113
112
  return
114
113
 
115
114
  if isinstance(exc_info[1], CELERY_CONTROL_FLOW_EXCEPTIONS):
116
- # ??? Doesn't map to anything
117
115
  _set_status("aborted")
118
116
  return
119
117
 
@@ -277,6 +275,7 @@ def _wrap_task_run(f):
277
275
  op=OP.QUEUE_SUBMIT_CELERY,
278
276
  name=task_name,
279
277
  origin=CeleryIntegration.origin,
278
+ only_if_parent=True,
280
279
  )
281
280
  if not task_started_from_beat
282
281
  else NoOpMgr()
@@ -307,40 +306,29 @@ def _wrap_tracer(task, f):
307
306
  with isolation_scope() as scope:
308
307
  scope._name = "celery"
309
308
  scope.clear_breadcrumbs()
309
+ scope.set_transaction_name(task.name, source=TransactionSource.TASK)
310
310
  scope.add_event_processor(_make_event_processor(task, *args, **kwargs))
311
311
 
312
- transaction = None
313
-
314
312
  # Celery task objects are not a thing to be trusted. Even
315
313
  # something such as attribute access can fail.
316
- with capture_internal_exceptions():
317
- headers = args[3].get("headers") or {}
318
- transaction = continue_trace(
319
- headers,
314
+ headers = args[3].get("headers") or {}
315
+
316
+ with sentry_sdk.continue_trace(headers):
317
+ with sentry_sdk.start_span(
320
318
  op=OP.QUEUE_TASK_CELERY,
321
- name="unknown celery task",
319
+ name=task.name,
322
320
  source=TransactionSource.TASK,
323
321
  origin=CeleryIntegration.origin,
324
- )
325
- transaction.name = task.name
326
- transaction.set_status(SPANSTATUS.OK)
322
+ # for some reason, args[1] is a list if non-empty but a
323
+ # tuple if empty
324
+ attributes=_prepopulate_attributes(task, list(args[1]), args[2]),
325
+ ) as root_span:
326
+ return_value = f(*args, **kwargs)
327
327
 
328
- if transaction is None:
329
- return f(*args, **kwargs)
328
+ if root_span.status is None:
329
+ root_span.set_status(SPANSTATUS.OK)
330
330
 
331
- with sentry_sdk.start_transaction(
332
- transaction,
333
- custom_sampling_context={
334
- "celery_job": {
335
- "task": task.name,
336
- # for some reason, args[1] is a list if non-empty but a
337
- # tuple if empty
338
- "args": list(args[1]),
339
- "kwargs": args[2],
340
- }
341
- },
342
- ):
343
- return f(*args, **kwargs)
331
+ return return_value
344
332
 
345
333
  return _inner # type: ignore
346
334
 
@@ -355,7 +343,7 @@ def _set_messaging_destination_name(task, span):
355
343
  if delivery_info.get("exchange") == "" and routing_key is not None:
356
344
  # Empty exchange indicates the default exchange, meaning the tasks
357
345
  # are sent to the queue with the same name as the routing key.
358
- span.set_data(SPANDATA.MESSAGING_DESTINATION_NAME, routing_key)
346
+ span.set_attribute(SPANDATA.MESSAGING_DESTINATION_NAME, routing_key)
359
347
 
360
348
 
361
349
  def _wrap_task_call(task, f):
@@ -377,6 +365,7 @@ def _wrap_task_call(task, f):
377
365
  op=OP.QUEUE_PROCESS,
378
366
  name=task.name,
379
367
  origin=CeleryIntegration.origin,
368
+ only_if_parent=True,
380
369
  ) as span:
381
370
  _set_messaging_destination_name(task, span)
382
371
 
@@ -391,23 +380,26 @@ def _wrap_task_call(task, f):
391
380
  )
392
381
 
393
382
  if latency is not None:
394
- span.set_data(SPANDATA.MESSAGING_MESSAGE_RECEIVE_LATENCY, latency)
383
+ span.set_attribute(
384
+ SPANDATA.MESSAGING_MESSAGE_RECEIVE_LATENCY, latency
385
+ )
395
386
 
396
387
  with capture_internal_exceptions():
397
- span.set_data(SPANDATA.MESSAGING_MESSAGE_ID, task.request.id)
388
+ span.set_attribute(SPANDATA.MESSAGING_MESSAGE_ID, task.request.id)
398
389
 
399
390
  with capture_internal_exceptions():
400
- span.set_data(
391
+ span.set_attribute(
401
392
  SPANDATA.MESSAGING_MESSAGE_RETRY_COUNT, task.request.retries
402
393
  )
403
394
 
404
395
  with capture_internal_exceptions():
405
- span.set_data(
396
+ span.set_attribute(
406
397
  SPANDATA.MESSAGING_SYSTEM,
407
398
  task.app.connection().transport.driver_type,
408
399
  )
409
400
 
410
401
  return f(*args, **kwargs)
402
+
411
403
  except Exception:
412
404
  exc_info = sys.exc_info()
413
405
  with capture_internal_exceptions():
@@ -506,23 +498,41 @@ def _patch_producer_publish():
506
498
  op=OP.QUEUE_PUBLISH,
507
499
  name=task_name,
508
500
  origin=CeleryIntegration.origin,
501
+ only_if_parent=True,
509
502
  ) as span:
510
503
  if task_id is not None:
511
- span.set_data(SPANDATA.MESSAGING_MESSAGE_ID, task_id)
504
+ span.set_attribute(SPANDATA.MESSAGING_MESSAGE_ID, task_id)
512
505
 
513
506
  if exchange == "" and routing_key is not None:
514
507
  # Empty exchange indicates the default exchange, meaning messages are
515
508
  # routed to the queue with the same name as the routing key.
516
- span.set_data(SPANDATA.MESSAGING_DESTINATION_NAME, routing_key)
509
+ span.set_attribute(SPANDATA.MESSAGING_DESTINATION_NAME, routing_key)
517
510
 
518
511
  if retries is not None:
519
- span.set_data(SPANDATA.MESSAGING_MESSAGE_RETRY_COUNT, retries)
512
+ span.set_attribute(SPANDATA.MESSAGING_MESSAGE_RETRY_COUNT, retries)
520
513
 
521
514
  with capture_internal_exceptions():
522
- span.set_data(
515
+ span.set_attribute(
523
516
  SPANDATA.MESSAGING_SYSTEM, self.connection.transport.driver_type
524
517
  )
525
518
 
526
519
  return original_publish(self, *args, **kwargs)
527
520
 
528
521
  Producer.publish = sentry_publish
522
+
523
+
524
+ def _prepopulate_attributes(task, args, kwargs):
525
+ # type: (Any, *Any, **Any) -> dict[str, str]
526
+ attributes = {
527
+ "celery.job.task": task.name,
528
+ }
529
+
530
+ for i, arg in enumerate(args):
531
+ with capture_internal_exceptions():
532
+ attributes[f"celery.job.args.{i}"] = str(arg)
533
+
534
+ for kwarg, value in kwargs.items():
535
+ with capture_internal_exceptions():
536
+ attributes[f"celery.job.kwargs.{kwarg}"] = str(value)
537
+
538
+ return attributes
@@ -3,9 +3,13 @@ from sentry_sdk.consts import OP, SPANDATA
3
3
  from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
4
4
  from sentry_sdk.tracing import Span
5
5
  from sentry_sdk.scope import should_send_default_pii
6
- from sentry_sdk.utils import capture_internal_exceptions, ensure_integration_enabled
6
+ from sentry_sdk.utils import (
7
+ _serialize_span_attribute,
8
+ capture_internal_exceptions,
9
+ ensure_integration_enabled,
10
+ )
7
11
 
8
- from typing import TYPE_CHECKING, TypeVar
12
+ from typing import TYPE_CHECKING, cast, Any, Dict, TypeVar
9
13
 
10
14
  # Hack to get new Python features working in older versions
11
15
  # without introducing a hard dependency on `typing_extensions`
@@ -84,19 +88,23 @@ def _wrap_start(f: Callable[P, T]) -> Callable[P, T]:
84
88
  op=OP.DB,
85
89
  name=query,
86
90
  origin=ClickhouseDriverIntegration.origin,
91
+ only_if_parent=True,
87
92
  )
88
93
 
89
94
  connection._sentry_span = span # type: ignore[attr-defined]
90
95
 
91
- _set_db_data(span, connection)
92
-
93
- span.set_data("query", query)
96
+ data = _get_db_data(connection)
97
+ data = cast("dict[str, Any]", data)
98
+ data["db.query.text"] = query
94
99
 
95
100
  if query_id:
96
- span.set_data("db.query_id", query_id)
101
+ data["db.query_id"] = query_id
97
102
 
98
103
  if params and should_send_default_pii():
99
- span.set_data("db.params", params)
104
+ data["db.params"] = params
105
+
106
+ connection._sentry_db_data = data # type: ignore[attr-defined]
107
+ _set_on_span(span, data)
100
108
 
101
109
  # run the original code
102
110
  ret = f(*args, **kwargs)
@@ -109,20 +117,32 @@ def _wrap_start(f: Callable[P, T]) -> Callable[P, T]:
109
117
  def _wrap_end(f: Callable[P, T]) -> Callable[P, T]:
110
118
  def _inner_end(*args: P.args, **kwargs: P.kwargs) -> T:
111
119
  res = f(*args, **kwargs)
112
- instance = args[0]
113
- span = getattr(instance.connection, "_sentry_span", None) # type: ignore[attr-defined]
120
+ client = cast("clickhouse_driver.client.Client", args[0])
121
+ connection = client.connection
114
122
 
123
+ span = getattr(connection, "_sentry_span", None)
115
124
  if span is not None:
125
+ data = getattr(connection, "_sentry_db_data", {})
126
+
116
127
  if res is not None and should_send_default_pii():
117
- span.set_data("db.result", res)
128
+ data["db.result"] = res
129
+ span.set_attribute("db.result", _serialize_span_attribute(res))
118
130
 
119
131
  with capture_internal_exceptions():
120
- span.scope.add_breadcrumb(
121
- message=span._data.pop("query"), category="query", data=span._data
122
- )
132
+ query = data.pop("db.query.text", None)
133
+ if query:
134
+ sentry_sdk.add_breadcrumb(
135
+ message=query, category="query", data=data
136
+ )
123
137
 
124
138
  span.finish()
125
139
 
140
+ try:
141
+ del connection._sentry_db_data
142
+ del connection._sentry_span
143
+ except AttributeError:
144
+ pass
145
+
126
146
  return res
127
147
 
128
148
  return _inner_end
@@ -130,28 +150,39 @@ def _wrap_end(f: Callable[P, T]) -> Callable[P, T]:
130
150
 
131
151
  def _wrap_send_data(f: Callable[P, T]) -> Callable[P, T]:
132
152
  def _inner_send_data(*args: P.args, **kwargs: P.kwargs) -> T:
133
- instance = args[0] # type: clickhouse_driver.client.Client
134
- data = args[2]
135
- span = getattr(instance.connection, "_sentry_span", None)
153
+ client = cast("clickhouse_driver.client.Client", args[0])
154
+ connection = client.connection
155
+ db_params_data = cast("list[Any]", args[2])
156
+ span = getattr(connection, "_sentry_span", None)
136
157
 
137
158
  if span is not None:
138
- _set_db_data(span, instance.connection)
159
+ data = _get_db_data(connection)
160
+ _set_on_span(span, data)
139
161
 
140
162
  if should_send_default_pii():
141
- db_params = span._data.get("db.params", [])
142
- db_params.extend(data)
143
- span.set_data("db.params", db_params)
163
+ saved_db_data = getattr(
164
+ connection, "_sentry_db_data", {}
165
+ ) # type: dict[str, Any]
166
+ db_params = saved_db_data.get("db.params") or [] # type: list[Any]
167
+ db_params.extend(db_params_data)
168
+ saved_db_data["db.params"] = db_params
169
+ span.set_attribute("db.params", _serialize_span_attribute(db_params))
144
170
 
145
171
  return f(*args, **kwargs)
146
172
 
147
173
  return _inner_send_data
148
174
 
149
175
 
150
- def _set_db_data(
151
- span: Span, connection: clickhouse_driver.connection.Connection
152
- ) -> None:
153
- span.set_data(SPANDATA.DB_SYSTEM, "clickhouse")
154
- span.set_data(SPANDATA.SERVER_ADDRESS, connection.host)
155
- span.set_data(SPANDATA.SERVER_PORT, connection.port)
156
- span.set_data(SPANDATA.DB_NAME, connection.database)
157
- span.set_data(SPANDATA.DB_USER, connection.user)
176
+ def _get_db_data(connection: clickhouse_driver.connection.Connection) -> Dict[str, str]:
177
+ return {
178
+ SPANDATA.DB_SYSTEM: "clickhouse",
179
+ SPANDATA.SERVER_ADDRESS: connection.host,
180
+ SPANDATA.SERVER_PORT: connection.port,
181
+ SPANDATA.DB_NAME: connection.database,
182
+ SPANDATA.DB_USER: connection.user,
183
+ }
184
+
185
+
186
+ def _set_on_span(span: Span, data: Dict[str, Any]) -> None:
187
+ for key, value in data.items():
188
+ span.set_attribute(key, _serialize_span_attribute(value))
@@ -147,6 +147,7 @@ def _wrap_chat(f, streaming):
147
147
  op=consts.OP.COHERE_CHAT_COMPLETIONS_CREATE,
148
148
  name="cohere.client.Chat",
149
149
  origin=CohereIntegration.origin,
150
+ only_if_parent=True,
150
151
  )
151
152
  span.__enter__()
152
153
  try:
@@ -233,6 +234,7 @@ def _wrap_embed(f):
233
234
  op=consts.OP.COHERE_EMBEDDINGS_CREATE,
234
235
  name="Cohere Embedding Creation",
235
236
  origin=CohereIntegration.origin,
237
+ only_if_parent=True,
236
238
  ) as span:
237
239
  if "texts" in kwargs and (
238
240
  should_send_default_pii() and integration.include_prompts
@@ -1,3 +1,4 @@
1
+ import functools
1
2
  import inspect
2
3
  import sys
3
4
  import threading
@@ -5,10 +6,9 @@ import weakref
5
6
  from importlib import import_module
6
7
 
7
8
  import sentry_sdk
8
- from sentry_sdk.consts import OP, SPANDATA
9
+ from sentry_sdk.consts import OP, SPANDATA, SOURCE_FOR_STYLE, TransactionSource
9
10
  from sentry_sdk.scope import add_global_event_processor, should_send_default_pii
10
11
  from sentry_sdk.serializer import add_global_repr_processor
11
- from sentry_sdk.tracing import SOURCE_FOR_STYLE, TransactionSource
12
12
  from sentry_sdk.tracing_utils import add_query_source, record_sql_queries
13
13
  from sentry_sdk.utils import (
14
14
  AnnotatedValue,
@@ -55,6 +55,7 @@ try:
55
55
  except ImportError:
56
56
  raise DidNotEnable("Django not installed")
57
57
 
58
+ from sentry_sdk.integrations.django.caching import patch_caching
58
59
  from sentry_sdk.integrations.django.transactions import LEGACY_RESOLVER
59
60
  from sentry_sdk.integrations.django.templates import (
60
61
  get_template_frame_from_exception,
@@ -64,11 +65,6 @@ from sentry_sdk.integrations.django.middleware import patch_django_middlewares
64
65
  from sentry_sdk.integrations.django.signals_handlers import patch_signals
65
66
  from sentry_sdk.integrations.django.views import patch_views
66
67
 
67
- if DJANGO_VERSION[:2] > (1, 8):
68
- from sentry_sdk.integrations.django.caching import patch_caching
69
- else:
70
- patch_caching = None # type: ignore
71
-
72
68
  from typing import TYPE_CHECKING
73
69
 
74
70
  if TYPE_CHECKING:
@@ -89,19 +85,6 @@ if TYPE_CHECKING:
89
85
  from sentry_sdk._types import Event, Hint, EventProcessor, NotImplementedType
90
86
 
91
87
 
92
- if DJANGO_VERSION < (1, 10):
93
-
94
- def is_authenticated(request_user):
95
- # type: (Any) -> bool
96
- return request_user.is_authenticated()
97
-
98
- else:
99
-
100
- def is_authenticated(request_user):
101
- # type: (Any) -> bool
102
- return request_user.is_authenticated
103
-
104
-
105
88
  TRANSACTION_STYLE_VALUES = ("function_name", "url")
106
89
 
107
90
 
@@ -131,7 +114,7 @@ class DjangoIntegration(Integration):
131
114
  transaction_style="url", # type: str
132
115
  middleware_spans=True, # type: bool
133
116
  signals_spans=True, # type: bool
134
- cache_spans=False, # type: bool
117
+ cache_spans=True, # type: bool
135
118
  signals_denylist=None, # type: Optional[list[signals.Signal]]
136
119
  http_methods_to_capture=DEFAULT_HTTP_METHODS_TO_CAPTURE, # type: tuple[str, ...]
137
120
  ):
@@ -321,6 +304,7 @@ def _patch_drf():
321
304
  else:
322
305
  old_drf_initial = APIView.initial
323
306
 
307
+ @functools.wraps(old_drf_initial)
324
308
  def sentry_patched_drf_initial(self, request, *args, **kwargs):
325
309
  # type: (APIView, Any, *Any, **Any) -> Any
326
310
  with capture_internal_exceptions():
@@ -413,11 +397,13 @@ def _set_transaction_name_and_source(scope, transaction_style, request):
413
397
  if hasattr(urlconf, "handler404"):
414
398
  handler = urlconf.handler404
415
399
  if isinstance(handler, str):
416
- scope.transaction = handler
400
+ scope.set_transaction_name(handler)
417
401
  else:
418
- scope.transaction = transaction_from_function(
402
+ name = transaction_from_function(
419
403
  getattr(handler, "view_class", handler)
420
404
  )
405
+ if isinstance(name, str):
406
+ scope.set_transaction_name(name)
421
407
  except Exception:
422
408
  pass
423
409
 
@@ -471,6 +457,7 @@ def _patch_get_response():
471
457
 
472
458
  old_get_response = BaseHandler.get_response
473
459
 
460
+ @functools.wraps(old_get_response)
474
461
  def sentry_patched_get_response(self, request):
475
462
  # type: (Any, WSGIRequest) -> Union[HttpResponse, BaseException]
476
463
  _before_get_response(request)
@@ -594,7 +581,7 @@ def _set_user_info(request, event):
594
581
 
595
582
  user = getattr(request, "user", None)
596
583
 
597
- if user is None or not is_authenticated(user):
584
+ if user is None or not user.is_authenticated:
598
585
  return
599
586
 
600
587
  try:
@@ -621,20 +608,11 @@ def install_sql_hook():
621
608
  except ImportError:
622
609
  from django.db.backends.util import CursorWrapper
623
610
 
624
- try:
625
- # django 1.6 and 1.7 compatability
626
- from django.db.backends import BaseDatabaseWrapper
627
- except ImportError:
628
- # django 1.8 or later
629
- from django.db.backends.base.base import BaseDatabaseWrapper
611
+ from django.db.backends.base.base import BaseDatabaseWrapper
630
612
 
631
- try:
632
- real_execute = CursorWrapper.execute
633
- real_executemany = CursorWrapper.executemany
634
- real_connect = BaseDatabaseWrapper.connect
635
- except AttributeError:
636
- # This won't work on Django versions < 1.6
637
- return
613
+ real_execute = CursorWrapper.execute
614
+ real_executemany = CursorWrapper.executemany
615
+ real_connect = BaseDatabaseWrapper.connect
638
616
 
639
617
  @ensure_integration_enabled(DjangoIntegration, real_execute)
640
618
  def execute(self, sql, params=None):
@@ -650,8 +628,8 @@ def install_sql_hook():
650
628
  _set_db_data(span, self)
651
629
  result = real_execute(self, sql, params)
652
630
 
653
- with capture_internal_exceptions():
654
- add_query_source(span)
631
+ with capture_internal_exceptions():
632
+ add_query_source(span)
655
633
 
656
634
  return result
657
635
 
@@ -670,8 +648,8 @@ def install_sql_hook():
670
648
 
671
649
  result = real_executemany(self, sql, param_list)
672
650
 
673
- with capture_internal_exceptions():
674
- add_query_source(span)
651
+ with capture_internal_exceptions():
652
+ add_query_source(span)
675
653
 
676
654
  return result
677
655
 
@@ -685,6 +663,7 @@ def install_sql_hook():
685
663
  op=OP.DB,
686
664
  name="connect",
687
665
  origin=DjangoIntegration.origin_db,
666
+ only_if_parent=True,
688
667
  ) as span:
689
668
  _set_db_data(span, self)
690
669
  return real_connect(self)
@@ -699,7 +678,7 @@ def _set_db_data(span, cursor_or_db):
699
678
  # type: (Span, Any) -> None
700
679
  db = cursor_or_db.db if hasattr(cursor_or_db, "db") else cursor_or_db
701
680
  vendor = db.vendor
702
- span.set_data(SPANDATA.DB_SYSTEM, vendor)
681
+ span.set_attribute(SPANDATA.DB_SYSTEM, vendor)
703
682
 
704
683
  # Some custom backends override `__getattr__`, making it look like `cursor_or_db`
705
684
  # actually has a `connection` and the `connection` has a `get_dsn_parameters`
@@ -732,16 +711,16 @@ def _set_db_data(span, cursor_or_db):
732
711
 
733
712
  db_name = connection_params.get("dbname") or connection_params.get("database")
734
713
  if db_name is not None:
735
- span.set_data(SPANDATA.DB_NAME, db_name)
714
+ span.set_attribute(SPANDATA.DB_NAME, db_name)
736
715
 
737
716
  server_address = connection_params.get("host")
738
717
  if server_address is not None:
739
- span.set_data(SPANDATA.SERVER_ADDRESS, server_address)
718
+ span.set_attribute(SPANDATA.SERVER_ADDRESS, server_address)
740
719
 
741
720
  server_port = connection_params.get("port")
742
721
  if server_port is not None:
743
- span.set_data(SPANDATA.SERVER_PORT, str(server_port))
722
+ span.set_attribute(SPANDATA.SERVER_PORT, str(server_port))
744
723
 
745
724
  server_socket_address = connection_params.get("unix_socket")
746
725
  if server_socket_address is not None:
747
- span.set_data(SPANDATA.SERVER_SOCKET_ADDRESS, server_socket_address)
726
+ span.set_attribute(SPANDATA.SERVER_SOCKET_ADDRESS, server_socket_address)
@@ -88,6 +88,7 @@ def patch_django_asgi_handler_impl(cls):
88
88
 
89
89
  old_app = cls.__call__
90
90
 
91
+ @functools.wraps(old_app)
91
92
  async def sentry_patched_asgi_handler(self, scope, receive, send):
92
93
  # type: (Any, Any, Any, Any) -> Any
93
94
  integration = sentry_sdk.get_client().get_integration(DjangoIntegration)
@@ -125,6 +126,7 @@ def patch_get_response_async(cls, _before_get_response):
125
126
  # type: (Any, Any) -> None
126
127
  old_get_response_async = cls.get_response_async
127
128
 
129
+ @functools.wraps(old_get_response_async)
128
130
  async def sentry_patched_get_response_async(self, request):
129
131
  # type: (Any, Any) -> Union[HttpResponse, BaseException]
130
132
  _before_get_response(request)
@@ -142,6 +144,7 @@ def patch_channels_asgi_handler_impl(cls):
142
144
  if channels.__version__ < "3.0.0":
143
145
  old_app = cls.__call__
144
146
 
147
+ @functools.wraps(old_app)
145
148
  async def sentry_patched_asgi_handler(self, receive, send):
146
149
  # type: (Any, Any, Any) -> Any
147
150
  integration = sentry_sdk.get_client().get_integration(DjangoIntegration)
@@ -173,8 +176,8 @@ def wrap_async_view(callback):
173
176
  async def sentry_wrapped_callback(request, *args, **kwargs):
174
177
  # type: (Any, *Any, **Any) -> Any
175
178
  current_scope = sentry_sdk.get_current_scope()
176
- if current_scope.transaction is not None:
177
- current_scope.transaction.update_active_thread()
179
+ if current_scope.root_span is not None:
180
+ current_scope.root_span.update_active_thread()
178
181
 
179
182
  sentry_scope = sentry_sdk.get_isolation_scope()
180
183
  if sentry_scope.profile is not None:
@@ -184,6 +187,7 @@ def wrap_async_view(callback):
184
187
  op=OP.VIEW_RENDER,
185
188
  name=request.resolver_match.view_name,
186
189
  origin=DjangoIntegration.origin,
190
+ only_if_parent=True,
187
191
  ):
188
192
  return await callback(request, *args, **kwargs)
189
193
 
@@ -54,27 +54,28 @@ def _patch_cache_method(cache, method_name, address, port):
54
54
  op=op,
55
55
  name=description,
56
56
  origin=DjangoIntegration.origin,
57
+ only_if_parent=True,
57
58
  ) as span:
58
59
  value = original_method(*args, **kwargs)
59
60
 
60
61
  with capture_internal_exceptions():
61
62
  if address is not None:
62
- span.set_data(SPANDATA.NETWORK_PEER_ADDRESS, address)
63
+ span.set_attribute(SPANDATA.NETWORK_PEER_ADDRESS, address)
63
64
 
64
65
  if port is not None:
65
- span.set_data(SPANDATA.NETWORK_PEER_PORT, port)
66
+ span.set_attribute(SPANDATA.NETWORK_PEER_PORT, port)
66
67
 
67
68
  key = _get_safe_key(method_name, args, kwargs)
68
69
  if key is not None:
69
- span.set_data(SPANDATA.CACHE_KEY, key)
70
+ span.set_attribute(SPANDATA.CACHE_KEY, key)
70
71
 
71
72
  item_size = None
72
73
  if is_get_operation:
73
74
  if value:
74
75
  item_size = len(str(value))
75
- span.set_data(SPANDATA.CACHE_HIT, True)
76
+ span.set_attribute(SPANDATA.CACHE_HIT, True)
76
77
  else:
77
- span.set_data(SPANDATA.CACHE_HIT, False)
78
+ span.set_attribute(SPANDATA.CACHE_HIT, False)
78
79
  else: # TODO: We don't handle `get_or_set` which we should
79
80
  arg_count = len(args)
80
81
  if arg_count >= 2:
@@ -85,7 +86,7 @@ def _patch_cache_method(cache, method_name, address, port):
85
86
  item_size = len(str(args[0]))
86
87
 
87
88
  if item_size is not None:
88
- span.set_data(SPANDATA.CACHE_ITEM_SIZE, item_size)
89
+ span.set_attribute(SPANDATA.CACHE_ITEM_SIZE, item_size)
89
90
 
90
91
  return value
91
92
 
@@ -133,22 +134,10 @@ def _get_address_port(settings):
133
134
  return address, int(port) if port is not None else None
134
135
 
135
136
 
136
- def should_enable_cache_spans():
137
- # type: () -> bool
138
- from sentry_sdk.integrations.django import DjangoIntegration
139
-
140
- client = sentry_sdk.get_client()
141
- integration = client.get_integration(DjangoIntegration)
142
- from django.conf import settings
143
-
144
- return integration is not None and (
145
- (client.spotlight is not None and settings.DEBUG is True)
146
- or integration.cache_spans is True
147
- )
148
-
149
-
150
137
  def patch_caching():
151
138
  # type: () -> None
139
+ from sentry_sdk.integrations.django import DjangoIntegration
140
+
152
141
  if not hasattr(CacheHandler, "_sentry_patched"):
153
142
  if DJANGO_VERSION < (3, 2):
154
143
  original_get_item = CacheHandler.__getitem__
@@ -158,7 +147,8 @@ def patch_caching():
158
147
  # type: (CacheHandler, str) -> Any
159
148
  cache = original_get_item(self, alias)
160
149
 
161
- if should_enable_cache_spans():
150
+ integration = sentry_sdk.get_client().get_integration(DjangoIntegration)
151
+ if integration is not None and integration.cache_spans:
162
152
  from django.conf import settings
163
153
 
164
154
  address, port = _get_address_port(
@@ -180,7 +170,8 @@ def patch_caching():
180
170
  # type: (CacheHandler, str) -> Any
181
171
  cache = original_create_connection(self, alias)
182
172
 
183
- if should_enable_cache_spans():
173
+ integration = sentry_sdk.get_client().get_integration(DjangoIntegration)
174
+ if integration is not None and integration.cache_spans:
184
175
  address, port = _get_address_port(self.settings[alias or "default"])
185
176
 
186
177
  _patch_cache(cache, address, port)
@@ -89,6 +89,7 @@ def _wrap_middleware(middleware, middleware_name):
89
89
  op=OP.MIDDLEWARE_DJANGO,
90
90
  name=description,
91
91
  origin=DjangoIntegration.origin,
92
+ only_if_parent=True,
92
93
  )
93
94
  middleware_span.set_tag("django.function_name", function_name)
94
95
  middleware_span.set_tag("django.middleware_name", middleware_name)