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
@@ -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)
@@ -50,6 +50,7 @@ def patch_signals():
50
50
 
51
51
  old_live_receivers = Signal._live_receivers
52
52
 
53
+ @wraps(old_live_receivers)
53
54
  def _sentry_live_receivers(self, sender):
54
55
  # type: (Signal, Any) -> Union[tuple[list[Callable[..., Any]], list[Callable[..., Any]]], list[Callable[..., Any]]]
55
56
  if DJANGO_VERSION >= (5, 0):
@@ -68,8 +69,9 @@ def patch_signals():
68
69
  op=OP.EVENT_DJANGO,
69
70
  name=signal_name,
70
71
  origin=DjangoIntegration.origin,
72
+ only_if_parent=True,
71
73
  ) as span:
72
- span.set_data("signal", signal_name)
74
+ span.set_attribute("signal", signal_name)
73
75
  return receiver(*args, **kwargs)
74
76
 
75
77
  return wrapper
@@ -1,8 +1,8 @@
1
1
  import functools
2
2
 
3
3
  from django.template import TemplateSyntaxError
4
+ from django.template.base import Origin
4
5
  from django.utils.safestring import mark_safe
5
- from django import VERSION as DJANGO_VERSION
6
6
 
7
7
  import sentry_sdk
8
8
  from sentry_sdk.consts import OP
@@ -17,13 +17,6 @@ if TYPE_CHECKING:
17
17
  from typing import Iterator
18
18
  from typing import Tuple
19
19
 
20
- try:
21
- # support Django 1.9
22
- from django.template.base import Origin
23
- except ImportError:
24
- # backward compatibility
25
- from django.template.loader import LoaderOrigin as Origin
26
-
27
20
 
28
21
  def get_template_frame_from_exception(exc_value):
29
22
  # type: (Optional[BaseException]) -> Optional[Dict[str, Any]]
@@ -72,14 +65,15 @@ def patch_templates():
72
65
  op=OP.TEMPLATE_RENDER,
73
66
  name=_get_template_name_description(self.template_name),
74
67
  origin=DjangoIntegration.origin,
68
+ only_if_parent=True,
75
69
  ) as span:
76
- span.set_data("context", self.context_data)
70
+ if isinstance(self.context_data, dict):
71
+ for k, v in self.context_data.items():
72
+ span.set_attribute(f"context.{k}", v)
77
73
  return real_rendered_content.fget(self)
78
74
 
79
75
  SimpleTemplateResponse.rendered_content = rendered_content
80
76
 
81
- if DJANGO_VERSION < (1, 7):
82
- return
83
77
  import django.shortcuts
84
78
 
85
79
  real_render = django.shortcuts.render
@@ -100,8 +94,10 @@ def patch_templates():
100
94
  op=OP.TEMPLATE_RENDER,
101
95
  name=_get_template_name_description(template_name),
102
96
  origin=DjangoIntegration.origin,
97
+ only_if_parent=True,
103
98
  ) as span:
104
- span.set_data("context", context)
99
+ for k, v in context.items():
100
+ span.set_attribute(f"context.{k}", v)
105
101
  return real_render(request, template_name, context, *args, **kwargs)
106
102
 
107
103
  django.shortcuts.render = render
@@ -19,12 +19,7 @@ if TYPE_CHECKING:
19
19
  from typing import Union
20
20
  from re import Pattern
21
21
 
22
- from django import VERSION as DJANGO_VERSION
23
-
24
- if DJANGO_VERSION >= (2, 0):
25
- from django.urls.resolvers import RoutePattern
26
- else:
27
- RoutePattern = None
22
+ from django.urls.resolvers import RoutePattern
28
23
 
29
24
  try:
30
25
  from django.urls import get_resolver
@@ -31,12 +31,14 @@ def patch_views():
31
31
  old_make_view_atomic = BaseHandler.make_view_atomic
32
32
  old_render = SimpleTemplateResponse.render
33
33
 
34
+ @functools.wraps(old_render)
34
35
  def sentry_patched_render(self):
35
36
  # type: (SimpleTemplateResponse) -> Any
36
37
  with sentry_sdk.start_span(
37
38
  op=OP.VIEW_RESPONSE_RENDER,
38
39
  name="serialize response",
39
40
  origin=DjangoIntegration.origin,
41
+ only_if_parent=True,
40
42
  ):
41
43
  return old_render(self)
42
44
 
@@ -77,8 +79,8 @@ def _wrap_sync_view(callback):
77
79
  def sentry_wrapped_callback(request, *args, **kwargs):
78
80
  # type: (Any, *Any, **Any) -> Any
79
81
  current_scope = sentry_sdk.get_current_scope()
80
- if current_scope.transaction is not None:
81
- current_scope.transaction.update_active_thread()
82
+ if current_scope.root_span is not None:
83
+ current_scope.root_span.update_active_thread()
82
84
 
83
85
  sentry_scope = sentry_sdk.get_isolation_scope()
84
86
  # set the active thread id to the handler thread for sync views
@@ -90,6 +92,7 @@ def _wrap_sync_view(callback):
90
92
  op=OP.VIEW_RENDER,
91
93
  name=request.resolver_match.view_name,
92
94
  origin=DjangoIntegration.origin,
95
+ only_if_parent=True,
93
96
  ):
94
97
  return callback(request, *args, **kwargs)
95
98
 
@@ -1,8 +1,8 @@
1
1
  import sentry_sdk
2
+ from sentry_sdk.consts import SOURCE_FOR_STYLE
2
3
  from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
3
4
  from sentry_sdk.integrations._wsgi_common import RequestExtractor
4
5
  from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
5
- from sentry_sdk.tracing import SOURCE_FOR_STYLE
6
6
  from sentry_sdk.utils import (
7
7
  capture_internal_exceptions,
8
8
  ensure_integration_enabled,
@@ -19,8 +19,6 @@ if TYPE_CHECKING:
19
19
 
20
20
  from sentry_sdk._types import Event, EventProcessor
21
21
 
22
- # In Falcon 3.0 `falcon.api_helpers` is renamed to `falcon.app_helpers`
23
- # and `falcon.API` to `falcon.App`
24
22
 
25
23
  try:
26
24
  import falcon # type: ignore
@@ -29,24 +27,15 @@ try:
29
27
  except ImportError:
30
28
  raise DidNotEnable("Falcon not installed")
31
29
 
32
- try:
33
- import falcon.app_helpers # type: ignore
34
-
35
- falcon_helpers = falcon.app_helpers
36
- falcon_app_class = falcon.App
37
- FALCON3 = True
38
- except ImportError:
39
- import falcon.api_helpers # type: ignore
30
+ import falcon.app_helpers # type: ignore
40
31
 
41
- falcon_helpers = falcon.api_helpers
42
- falcon_app_class = falcon.API
43
- FALCON3 = False
32
+ falcon_helpers = falcon.app_helpers
33
+ falcon_app_class = falcon.App
44
34
 
45
35
 
46
36
  _FALCON_UNSET = None # type: Optional[object]
47
- if FALCON3: # falcon.request._UNSET is only available in Falcon 3.0+
48
- with capture_internal_exceptions():
49
- from falcon.request import _UNSET as _FALCON_UNSET # type: ignore[import-not-found, no-redef]
37
+ with capture_internal_exceptions():
38
+ from falcon.request import _UNSET as _FALCON_UNSET # type: ignore[import-not-found, no-redef]
50
39
 
51
40
 
52
41
  class FalconRequestExtractor(RequestExtractor):
@@ -232,14 +221,7 @@ def _exception_leads_to_http_5xx(ex, response):
232
221
  ex, (falcon.HTTPError, falcon.http_status.HTTPStatus)
233
222
  )
234
223
 
235
- # We only check the HTTP status on Falcon 3 because in Falcon 2, the status on the response
236
- # at the stage where we capture it is listed as 200, even though we would expect to see a 500
237
- # status. Since at the time of this change, Falcon 2 is ca. 4 years old, we have decided to
238
- # only perform this check on Falcon 3+, despite the risk that some handled errors might be
239
- # reported to Sentry as unhandled on Falcon 2.
240
- return (is_server_error or is_unhandled_error) and (
241
- not FALCON3 or _has_http_5xx_status(response)
242
- )
224
+ return (is_server_error or is_unhandled_error) and _has_http_5xx_status(response)
243
225
 
244
226
 
245
227
  def _has_http_5xx_status(response):