Skip to content

Commit a3e37c7

Browse files
committed
Merge branch 'master' into skalinchuk/master
2 parents 28d36b5 + 5de66e2 commit a3e37c7

File tree

5 files changed

+1117
-3
lines changed

5 files changed

+1117
-3
lines changed

sentry_sdk/consts.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@ class INSTRUMENTER:
114114
OTEL = "otel"
115115

116116

117+
class SPANNAME:
118+
DB_COMMIT = "COMMIT"
119+
DB_ROLLBACK = "ROLLBACK"
120+
121+
117122
class SPANDATA:
118123
"""
119124
Additional information describing the type of the span.

sentry_sdk/integrations/django/__init__.py

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from importlib import import_module
66

77
import sentry_sdk
8-
from sentry_sdk.consts import OP, SPANDATA
8+
from sentry_sdk.consts import OP, SPANDATA, SPANNAME
99
from sentry_sdk.scope import add_global_event_processor, should_send_default_pii
1010
from sentry_sdk.serializer import add_global_repr_processor, add_repr_sequence_type
1111
from sentry_sdk.tracing import SOURCE_FOR_STYLE, TransactionSource
@@ -132,6 +132,7 @@ def __init__(
132132
middleware_spans=True, # type: bool
133133
signals_spans=True, # type: bool
134134
cache_spans=False, # type: bool
135+
db_transaction_spans=False, # type: bool
135136
signals_denylist=None, # type: Optional[list[signals.Signal]]
136137
http_methods_to_capture=DEFAULT_HTTP_METHODS_TO_CAPTURE, # type: tuple[str, ...]
137138
):
@@ -148,6 +149,7 @@ def __init__(
148149
self.signals_denylist = signals_denylist or []
149150

150151
self.cache_spans = cache_spans
152+
self.db_transaction_spans = db_transaction_spans
151153

152154
self.http_methods_to_capture = tuple(map(str.upper, http_methods_to_capture))
153155

@@ -633,6 +635,8 @@ def install_sql_hook():
633635
real_execute = CursorWrapper.execute
634636
real_executemany = CursorWrapper.executemany
635637
real_connect = BaseDatabaseWrapper.connect
638+
real_commit = BaseDatabaseWrapper._commit
639+
real_rollback = BaseDatabaseWrapper._rollback
636640
except AttributeError:
637641
# This won't work on Django versions < 1.6
638642
return
@@ -690,18 +694,53 @@ def connect(self):
690694
_set_db_data(span, self)
691695
return real_connect(self)
692696

697+
def _commit(self):
698+
# type: (BaseDatabaseWrapper) -> None
699+
integration = sentry_sdk.get_client().get_integration(DjangoIntegration)
700+
701+
if integration is None or not integration.db_transaction_spans:
702+
return real_commit(self)
703+
704+
with sentry_sdk.start_span(
705+
op=OP.DB,
706+
name=SPANNAME.DB_COMMIT,
707+
origin=DjangoIntegration.origin_db,
708+
) as span:
709+
_set_db_data(span, self, SPANNAME.DB_COMMIT)
710+
return real_commit(self)
711+
712+
def _rollback(self):
713+
# type: (BaseDatabaseWrapper) -> None
714+
integration = sentry_sdk.get_client().get_integration(DjangoIntegration)
715+
716+
if integration is None or not integration.db_transaction_spans:
717+
return real_rollback(self)
718+
719+
with sentry_sdk.start_span(
720+
op=OP.DB,
721+
name=SPANNAME.DB_ROLLBACK,
722+
origin=DjangoIntegration.origin_db,
723+
) as span:
724+
_set_db_data(span, self, SPANNAME.DB_ROLLBACK)
725+
return real_rollback(self)
726+
693727
CursorWrapper.execute = execute
694728
CursorWrapper.executemany = executemany
695729
BaseDatabaseWrapper.connect = connect
730+
BaseDatabaseWrapper._commit = _commit
731+
BaseDatabaseWrapper._rollback = _rollback
696732
ignore_logger("django.db.backends")
697733

698734

699-
def _set_db_data(span, cursor_or_db):
700-
# type: (Span, Any) -> None
735+
def _set_db_data(span, cursor_or_db, db_operation=None):
736+
# type: (Span, Any, Optional[str]) -> None
701737
db = cursor_or_db.db if hasattr(cursor_or_db, "db") else cursor_or_db
702738
vendor = db.vendor
703739
span.set_data(SPANDATA.DB_SYSTEM, vendor)
704740

741+
if db_operation is not None:
742+
span.set_data(SPANDATA.DB_OPERATION, db_operation)
743+
705744
# Some custom backends override `__getattr__`, making it look like `cursor_or_db`
706745
# actually has a `connection` and the `connection` has a `get_dsn_parameters`
707746
# attribute, only to throw an error once you actually want to call it.

tests/integrations/django/myapp/urls.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,31 @@ def path(path, *args, **kwargs):
6161
path("template-test4", views.template_test4, name="template_test4"),
6262
path("postgres-select", views.postgres_select, name="postgres_select"),
6363
path("postgres-select-slow", views.postgres_select_orm, name="postgres_select_orm"),
64+
path(
65+
"postgres-insert-no-autocommit",
66+
views.postgres_insert_orm_no_autocommit,
67+
name="postgres_insert_orm_no_autocommit",
68+
),
69+
path(
70+
"postgres-insert-no-autocommit-rollback",
71+
views.postgres_insert_orm_no_autocommit_rollback,
72+
name="postgres_insert_orm_no_autocommit_rollback",
73+
),
74+
path(
75+
"postgres-insert-atomic",
76+
views.postgres_insert_orm_atomic,
77+
name="postgres_insert_orm_atomic",
78+
),
79+
path(
80+
"postgres-insert-atomic-rollback",
81+
views.postgres_insert_orm_atomic_rollback,
82+
name="postgres_insert_orm_atomic_rollback",
83+
),
84+
path(
85+
"postgres-insert-atomic-exception",
86+
views.postgres_insert_orm_atomic_exception,
87+
name="postgres_insert_orm_atomic_exception",
88+
),
6489
path(
6590
"postgres-select-slow-from-supplement",
6691
helper_views.postgres_select_orm,

tests/integrations/django/myapp/views.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import json
33
import threading
44

5+
from django.db import transaction
56
from django.contrib.auth import login
67
from django.contrib.auth.models import User
78
from django.core.exceptions import PermissionDenied
@@ -246,6 +247,73 @@ def postgres_select_orm(request, *args, **kwargs):
246247
return HttpResponse("ok {}".format(user))
247248

248249

250+
@csrf_exempt
251+
def postgres_insert_orm_no_autocommit(request, *args, **kwargs):
252+
transaction.set_autocommit(False, using="postgres")
253+
try:
254+
user = User.objects.db_manager("postgres").create_user(
255+
username="user1",
256+
)
257+
transaction.commit(using="postgres")
258+
except Exception:
259+
transaction.rollback(using="postgres")
260+
transaction.set_autocommit(True, using="postgres")
261+
raise
262+
263+
transaction.set_autocommit(True, using="postgres")
264+
return HttpResponse("ok {}".format(user))
265+
266+
267+
@csrf_exempt
268+
def postgres_insert_orm_no_autocommit_rollback(request, *args, **kwargs):
269+
transaction.set_autocommit(False, using="postgres")
270+
try:
271+
user = User.objects.db_manager("postgres").create_user(
272+
username="user1",
273+
)
274+
transaction.rollback(using="postgres")
275+
except Exception:
276+
transaction.rollback(using="postgres")
277+
transaction.set_autocommit(True, using="postgres")
278+
raise
279+
280+
transaction.set_autocommit(True, using="postgres")
281+
return HttpResponse("ok {}".format(user))
282+
283+
284+
@csrf_exempt
285+
def postgres_insert_orm_atomic(request, *args, **kwargs):
286+
with transaction.atomic(using="postgres"):
287+
user = User.objects.db_manager("postgres").create_user(
288+
username="user1",
289+
)
290+
return HttpResponse("ok {}".format(user))
291+
292+
293+
@csrf_exempt
294+
def postgres_insert_orm_atomic_rollback(request, *args, **kwargs):
295+
with transaction.atomic(using="postgres"):
296+
user = User.objects.db_manager("postgres").create_user(
297+
username="user1",
298+
)
299+
transaction.set_rollback(True, using="postgres")
300+
return HttpResponse("ok {}".format(user))
301+
302+
303+
@csrf_exempt
304+
def postgres_insert_orm_atomic_exception(request, *args, **kwargs):
305+
try:
306+
with transaction.atomic(using="postgres"):
307+
user = User.objects.db_manager("postgres").create_user(
308+
username="user1",
309+
)
310+
transaction.set_rollback(True, using="postgres")
311+
1 / 0
312+
except ZeroDivisionError:
313+
pass
314+
return HttpResponse("ok {}".format(user))
315+
316+
249317
@csrf_exempt
250318
def permission_denied_exc(*args, **kwargs):
251319
raise PermissionDenied("bye")

0 commit comments

Comments
 (0)