From 353bde601f6ce47be318c71d1f1b3b86d61babee Mon Sep 17 00:00:00 2001 From: Tony Xiao Date: Thu, 17 Oct 2024 15:16:17 -0400 Subject: [PATCH 1/4] fix(profiling): Update active thread for asgi Ensure the handling thread is set on the transaction for asgi transactions not just main thread. --- sentry_sdk/integrations/django/asgi.py | 2 ++ sentry_sdk/integrations/django/views.py | 2 ++ sentry_sdk/integrations/fastapi.py | 2 ++ sentry_sdk/integrations/starlette.py | 2 ++ sentry_sdk/tracing.py | 8 ++++++-- 5 files changed, 14 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/integrations/django/asgi.py b/sentry_sdk/integrations/django/asgi.py index bcc83b8e59..7cf9090fc9 100644 --- a/sentry_sdk/integrations/django/asgi.py +++ b/sentry_sdk/integrations/django/asgi.py @@ -171,6 +171,8 @@ async def sentry_wrapped_callback(request, *args, **kwargs): sentry_scope = sentry_sdk.get_isolation_scope() if sentry_scope.profile is not None: sentry_scope.profile.update_active_thread_id() + if sentry_scope.transaction is not None: + sentry_scope.transaction.update_active_thread() with sentry_sdk.start_span( op=OP.VIEW_RENDER, diff --git a/sentry_sdk/integrations/django/views.py b/sentry_sdk/integrations/django/views.py index cb81d3555c..538367bd27 100644 --- a/sentry_sdk/integrations/django/views.py +++ b/sentry_sdk/integrations/django/views.py @@ -81,6 +81,8 @@ def sentry_wrapped_callback(request, *args, **kwargs): # this isn't necessary for async views since that runs on main if sentry_scope.profile is not None: sentry_scope.profile.update_active_thread_id() + if sentry_scope.transaction is not None: + sentry_scope.transaction.update_active_thread() with sentry_sdk.start_span( op=OP.VIEW_RENDER, diff --git a/sentry_sdk/integrations/fastapi.py b/sentry_sdk/integrations/fastapi.py index c3816b6565..b5a954d21e 100644 --- a/sentry_sdk/integrations/fastapi.py +++ b/sentry_sdk/integrations/fastapi.py @@ -91,6 +91,8 @@ def _sentry_call(*args, **kwargs): sentry_scope = sentry_sdk.get_isolation_scope() if sentry_scope.profile is not None: sentry_scope.profile.update_active_thread_id() + if sentry_scope.transaction is not None: + sentry_scope.transaction.update_active_thread() return old_call(*args, **kwargs) dependant.call = _sentry_call diff --git a/sentry_sdk/integrations/starlette.py b/sentry_sdk/integrations/starlette.py index 03584fdad7..1ad7ec27c6 100644 --- a/sentry_sdk/integrations/starlette.py +++ b/sentry_sdk/integrations/starlette.py @@ -491,6 +491,8 @@ def _sentry_sync_func(*args, **kwargs): if sentry_scope.profile is not None: sentry_scope.profile.update_active_thread_id() + if sentry_scope.transaction is not None: + sentry_scope.transaction.update_active_thread() request = args[0] diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index 7ce577b1d0..3868b2e6c8 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -329,8 +329,7 @@ def __init__( self._span_recorder = None # type: Optional[_SpanRecorder] self._local_aggregator = None # type: Optional[LocalAggregator] - thread_id, thread_name = get_current_thread_meta() - self.set_thread(thread_id, thread_name) + self.update_active_thread() self.set_profiler_id(get_profiler_id()) # TODO this should really live on the Transaction class rather than the Span @@ -732,6 +731,11 @@ def get_profile_context(self): "profiler_id": profiler_id, } + def update_active_thread(self): + # type: () -> None + thread_id, thread_name = get_current_thread_meta() + self.set_thread(thread_id, thread_name) + class Transaction(Span): """The Transaction is the root element that holds all the spans From 806992f187d18ad0f8a292ed922abd1e66bdda8a Mon Sep 17 00:00:00 2001 From: Tony Xiao Date: Fri, 18 Oct 2024 16:18:01 -0400 Subject: [PATCH 2/4] update active thread id --- sentry_sdk/integrations/django/asgi.py | 6 ++++-- sentry_sdk/integrations/django/views.py | 6 ++++-- sentry_sdk/integrations/fastapi.py | 7 +++++-- sentry_sdk/integrations/quart.py | 13 +++++++------ sentry_sdk/integrations/starlette.py | 7 ++++--- tests/integrations/django/asgi/test_asgi.py | 14 +++++++++++--- tests/integrations/fastapi/test_fastapi.py | 14 +++++++++++--- tests/integrations/quart/test_quart.py | 19 ++++++++++++++----- .../integrations/starlette/test_starlette.py | 14 +++++++++++--- 9 files changed, 71 insertions(+), 29 deletions(-) diff --git a/sentry_sdk/integrations/django/asgi.py b/sentry_sdk/integrations/django/asgi.py index 6e9aa4d04b..73a25acc9f 100644 --- a/sentry_sdk/integrations/django/asgi.py +++ b/sentry_sdk/integrations/django/asgi.py @@ -172,11 +172,13 @@ def wrap_async_view(callback): @functools.wraps(callback) async def sentry_wrapped_callback(request, *args, **kwargs): # type: (Any, *Any, **Any) -> Any + current_scope = sentry_sdk.get_current_scope() + if current_scope.transaction is not None: + current_scope.transaction.update_active_thread() + sentry_scope = sentry_sdk.get_isolation_scope() if sentry_scope.profile is not None: sentry_scope.profile.update_active_thread_id() - if sentry_scope.transaction is not None: - sentry_scope.transaction.update_active_thread() with sentry_sdk.start_span( op=OP.VIEW_RENDER, diff --git a/sentry_sdk/integrations/django/views.py b/sentry_sdk/integrations/django/views.py index 538367bd27..0a9861a6a6 100644 --- a/sentry_sdk/integrations/django/views.py +++ b/sentry_sdk/integrations/django/views.py @@ -76,13 +76,15 @@ def _wrap_sync_view(callback): @functools.wraps(callback) def sentry_wrapped_callback(request, *args, **kwargs): # type: (Any, *Any, **Any) -> Any + current_scope = sentry_sdk.get_current_scope() + if current_scope.transaction is not None: + current_scope.transaction.update_active_thread() + sentry_scope = sentry_sdk.get_isolation_scope() # set the active thread id to the handler thread for sync views # this isn't necessary for async views since that runs on main if sentry_scope.profile is not None: sentry_scope.profile.update_active_thread_id() - if sentry_scope.transaction is not None: - sentry_scope.transaction.update_active_thread() with sentry_sdk.start_span( op=OP.VIEW_RENDER, diff --git a/sentry_sdk/integrations/fastapi.py b/sentry_sdk/integrations/fastapi.py index b5a954d21e..8877925a36 100644 --- a/sentry_sdk/integrations/fastapi.py +++ b/sentry_sdk/integrations/fastapi.py @@ -88,11 +88,14 @@ def _sentry_get_request_handler(*args, **kwargs): @wraps(old_call) def _sentry_call(*args, **kwargs): # type: (*Any, **Any) -> Any + current_scope = sentry_sdk.get_current_scope() + if current_scope.transaction is not None: + current_scope.transaction.update_active_thread() + sentry_scope = sentry_sdk.get_isolation_scope() if sentry_scope.profile is not None: sentry_scope.profile.update_active_thread_id() - if sentry_scope.transaction is not None: - sentry_scope.transaction.update_active_thread() + return old_call(*args, **kwargs) dependant.call = _sentry_call diff --git a/sentry_sdk/integrations/quart.py b/sentry_sdk/integrations/quart.py index ac58f21175..51306bb4cd 100644 --- a/sentry_sdk/integrations/quart.py +++ b/sentry_sdk/integrations/quart.py @@ -1,6 +1,5 @@ import asyncio import inspect -import threading from functools import wraps import sentry_sdk @@ -122,11 +121,13 @@ def decorator(old_func): @ensure_integration_enabled(QuartIntegration, old_func) def _sentry_func(*args, **kwargs): # type: (*Any, **Any) -> Any - scope = sentry_sdk.get_isolation_scope() - if scope.profile is not None: - scope.profile.active_thread_id = ( - threading.current_thread().ident - ) + current_scope = sentry_sdk.get_current_scope() + if current_scope.transaction is not None: + current_scope.transaction.update_active_thread() + + sentry_scope = sentry_sdk.get_isolation_scope() + if sentry_scope.profile is not None: + sentry_scope.profile.update_active_thread_id() return old_func(*args, **kwargs) diff --git a/sentry_sdk/integrations/starlette.py b/sentry_sdk/integrations/starlette.py index 1ad7ec27c6..52c64f6843 100644 --- a/sentry_sdk/integrations/starlette.py +++ b/sentry_sdk/integrations/starlette.py @@ -487,12 +487,13 @@ def _sentry_sync_func(*args, **kwargs): if integration is None: return old_func(*args, **kwargs) - sentry_scope = sentry_sdk.get_isolation_scope() + current_scope = sentry_sdk.get_current_scope() + if current_scope.transaction is not None: + current_scope.transaction.update_active_thread() + sentry_scope = sentry_sdk.get_isolation_scope() if sentry_scope.profile is not None: sentry_scope.profile.update_active_thread_id() - if sentry_scope.transaction is not None: - sentry_scope.transaction.update_active_thread() request = args[0] diff --git a/tests/integrations/django/asgi/test_asgi.py b/tests/integrations/django/asgi/test_asgi.py index f6cfae0d2c..06d14ce093 100644 --- a/tests/integrations/django/asgi/test_asgi.py +++ b/tests/integrations/django/asgi/test_asgi.py @@ -111,7 +111,7 @@ async def test_active_thread_id(sentry_init, capture_envelopes, endpoint, applic sentry_init( integrations=[DjangoIntegration()], traces_sample_rate=1.0, - _experiments={"profiles_sample_rate": 1.0}, + profiles_sample_rate=1.0, ) envelopes = capture_envelopes() @@ -128,11 +128,19 @@ async def test_active_thread_id(sentry_init, capture_envelopes, endpoint, applic data = json.loads(response["body"]) - for profile in profiles: - transactions = profile.payload.json["transactions"] + for item in profiles: + transactions = item.payload.json["transactions"] assert len(transactions) == 1 assert str(data["active"]) == transactions[0]["active_thread_id"] + transactions = [item for item in envelopes[0].items if item.type == "transaction"] + assert len(transactions) == 1 + + for item in transactions: + transaction = item.payload.json + trace_context = transaction["contexts"]["trace"] + assert str(data["active"]) == trace_context["data"]["thread.id"] + @pytest.mark.asyncio @pytest.mark.forked diff --git a/tests/integrations/fastapi/test_fastapi.py b/tests/integrations/fastapi/test_fastapi.py index 93d048c029..97aea06344 100644 --- a/tests/integrations/fastapi/test_fastapi.py +++ b/tests/integrations/fastapi/test_fastapi.py @@ -184,7 +184,7 @@ def test_legacy_setup( def test_active_thread_id(sentry_init, capture_envelopes, teardown_profiling, endpoint): sentry_init( traces_sample_rate=1.0, - _experiments={"profiles_sample_rate": 1.0}, + profiles_sample_rate=1.0, ) app = fastapi_app_factory() asgi_app = SentryAsgiMiddleware(app) @@ -203,11 +203,19 @@ def test_active_thread_id(sentry_init, capture_envelopes, teardown_profiling, en profiles = [item for item in envelopes[0].items if item.type == "profile"] assert len(profiles) == 1 - for profile in profiles: - transactions = profile.payload.json["transactions"] + for item in profiles: + transactions = item.payload.json["transactions"] assert len(transactions) == 1 assert str(data["active"]) == transactions[0]["active_thread_id"] + transactions = [item for item in envelopes[0].items if item.type == "transaction"] + assert len(transactions) == 1 + + for item in transactions: + transaction = item.payload.json + trace_context = transaction["contexts"]["trace"] + assert str(data["active"]) == trace_context["data"]["thread.id"] + @pytest.mark.asyncio async def test_original_request_not_scrubbed(sentry_init, capture_events): diff --git a/tests/integrations/quart/test_quart.py b/tests/integrations/quart/test_quart.py index 321f07e3c6..27f82e3014 100644 --- a/tests/integrations/quart/test_quart.py +++ b/tests/integrations/quart/test_quart.py @@ -523,10 +523,11 @@ async def dispatch_request(self): @pytest.mark.parametrize("endpoint", ["/sync/thread_ids", "/async/thread_ids"]) +@pytest.mark.asyncio async def test_active_thread_id(sentry_init, capture_envelopes, endpoint, app): sentry_init( traces_sample_rate=1.0, - _experiments={"profiles_sample_rate": 1.0}, + profiles_sample_rate=1.0, ) envelopes = capture_envelopes() @@ -535,19 +536,27 @@ async def test_active_thread_id(sentry_init, capture_envelopes, endpoint, app): response = await client.get(endpoint) assert response.status_code == 200 - data = json.loads(response.content) + data = json.loads(await response.get_data(as_text=True)) envelopes = [envelope for envelope in envelopes] assert len(envelopes) == 1 profiles = [item for item in envelopes[0].items if item.type == "profile"] - assert len(profiles) == 1 + assert len(profiles) == 1, envelopes[0].items - for profile in profiles: - transactions = profile.payload.json["transactions"] + for item in profiles: + transactions = item.payload.json["transactions"] assert len(transactions) == 1 assert str(data["active"]) == transactions[0]["active_thread_id"] + transactions = [item for item in envelopes[0].items if item.type == "transaction"] + assert len(transactions) == 1 + + for item in transactions: + transaction = item.payload.json + trace_context = transaction["contexts"]["trace"] + assert str(data["active"]) == trace_context["data"]["thread.id"] + @pytest.mark.asyncio async def test_span_origin(sentry_init, capture_events, app): diff --git a/tests/integrations/starlette/test_starlette.py b/tests/integrations/starlette/test_starlette.py index 1ba9eb7589..fd47895f5a 100644 --- a/tests/integrations/starlette/test_starlette.py +++ b/tests/integrations/starlette/test_starlette.py @@ -885,7 +885,7 @@ def test_legacy_setup( def test_active_thread_id(sentry_init, capture_envelopes, teardown_profiling, endpoint): sentry_init( traces_sample_rate=1.0, - _experiments={"profiles_sample_rate": 1.0}, + profiles_sample_rate=1.0, ) app = starlette_app_factory() asgi_app = SentryAsgiMiddleware(app) @@ -904,11 +904,19 @@ def test_active_thread_id(sentry_init, capture_envelopes, teardown_profiling, en profiles = [item for item in envelopes[0].items if item.type == "profile"] assert len(profiles) == 1 - for profile in profiles: - transactions = profile.payload.json["transactions"] + for item in profiles: + transactions = item.payload.json["transactions"] assert len(transactions) == 1 assert str(data["active"]) == transactions[0]["active_thread_id"] + transactions = [item for item in envelopes[0].items if item.type == "transaction"] + assert len(transactions) == 1 + + for item in transactions: + transaction = item.payload.json + trace_context = transaction["contexts"]["trace"] + assert str(data["active"]) == trace_context["data"]["thread.id"] + def test_original_request_not_scrubbed(sentry_init, capture_events): sentry_init(integrations=[StarletteIntegration()]) From 4b41f914be7acd782f01439ab7f56e7c6e69e454 Mon Sep 17 00:00:00 2001 From: Tony Xiao Date: Fri, 18 Oct 2024 17:28:05 -0400 Subject: [PATCH 3/4] fix quart --- tests/integrations/quart/test_quart.py | 74 ++++++++++++++++---------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/tests/integrations/quart/test_quart.py b/tests/integrations/quart/test_quart.py index 27f82e3014..146de7dfcc 100644 --- a/tests/integrations/quart/test_quart.py +++ b/tests/integrations/quart/test_quart.py @@ -1,8 +1,8 @@ import json import threading +from unittest import mock import pytest -import pytest_asyncio import sentry_sdk from sentry_sdk import ( @@ -28,8 +28,7 @@ auth_manager = AuthManager() -@pytest_asyncio.fixture -async def app(): +def quart_app_factory(): app = Quart(__name__) app.debug = False app.config["TESTING"] = False @@ -73,8 +72,9 @@ def integration_enabled_params(request): @pytest.mark.asyncio -async def test_has_context(sentry_init, app, capture_events): +async def test_has_context(sentry_init, capture_events): sentry_init(integrations=[quart_sentry.QuartIntegration()]) + app = quart_app_factory() events = capture_events() client = app.test_client() @@ -99,7 +99,6 @@ async def test_has_context(sentry_init, app, capture_events): ) async def test_transaction_style( sentry_init, - app, capture_events, url, transaction_style, @@ -111,6 +110,7 @@ async def test_transaction_style( quart_sentry.QuartIntegration(transaction_style=transaction_style) ] ) + app = quart_app_factory() events = capture_events() client = app.test_client() @@ -126,10 +126,10 @@ async def test_errors( sentry_init, capture_exceptions, capture_events, - app, integration_enabled_params, ): sentry_init(**integration_enabled_params) + app = quart_app_factory() @app.route("/") async def index(): @@ -153,9 +153,10 @@ async def index(): @pytest.mark.asyncio async def test_quart_auth_not_installed( - sentry_init, app, capture_events, monkeypatch, integration_enabled_params + sentry_init, capture_events, monkeypatch, integration_enabled_params ): sentry_init(**integration_enabled_params) + app = quart_app_factory() monkeypatch.setattr(quart_sentry, "quart_auth", None) @@ -170,9 +171,10 @@ async def test_quart_auth_not_installed( @pytest.mark.asyncio async def test_quart_auth_not_configured( - sentry_init, app, capture_events, monkeypatch, integration_enabled_params + sentry_init, capture_events, monkeypatch, integration_enabled_params ): sentry_init(**integration_enabled_params) + app = quart_app_factory() assert quart_sentry.quart_auth @@ -186,9 +188,10 @@ async def test_quart_auth_not_configured( @pytest.mark.asyncio async def test_quart_auth_partially_configured( - sentry_init, app, capture_events, monkeypatch, integration_enabled_params + sentry_init, capture_events, monkeypatch, integration_enabled_params ): sentry_init(**integration_enabled_params) + app = quart_app_factory() events = capture_events() @@ -205,13 +208,13 @@ async def test_quart_auth_partially_configured( async def test_quart_auth_configured( send_default_pii, sentry_init, - app, user_id, capture_events, monkeypatch, integration_enabled_params, ): sentry_init(send_default_pii=send_default_pii, **integration_enabled_params) + app = quart_app_factory() @app.route("/login") async def login(): @@ -242,10 +245,9 @@ async def login(): [quart_sentry.QuartIntegration(), LoggingIntegration(event_level="ERROR")], ], ) -async def test_errors_not_reported_twice( - sentry_init, integrations, capture_events, app -): +async def test_errors_not_reported_twice(sentry_init, integrations, capture_events): sentry_init(integrations=integrations) + app = quart_app_factory() @app.route("/") async def index(): @@ -265,7 +267,7 @@ async def index(): @pytest.mark.asyncio -async def test_logging(sentry_init, capture_events, app): +async def test_logging(sentry_init, capture_events): # ensure that Quart's logger magic doesn't break ours sentry_init( integrations=[ @@ -273,6 +275,7 @@ async def test_logging(sentry_init, capture_events, app): LoggingIntegration(event_level="ERROR"), ] ) + app = quart_app_factory() @app.route("/") async def index(): @@ -289,13 +292,17 @@ async def index(): @pytest.mark.asyncio -async def test_no_errors_without_request(app, sentry_init): +async def test_no_errors_without_request(sentry_init): sentry_init(integrations=[quart_sentry.QuartIntegration()]) + app = quart_app_factory() + async with app.app_context(): capture_exception(ValueError()) -def test_cli_commands_raise(app): +def test_cli_commands_raise(): + app = quart_app_factory() + if not hasattr(app, "cli"): pytest.skip("Too old quart version") @@ -312,8 +319,9 @@ def foo(): @pytest.mark.asyncio -async def test_500(sentry_init, app): +async def test_500(sentry_init): sentry_init(integrations=[quart_sentry.QuartIntegration()]) + app = quart_app_factory() @app.route("/") async def index(): @@ -330,8 +338,9 @@ async def error_handler(err): @pytest.mark.asyncio -async def test_error_in_errorhandler(sentry_init, capture_events, app): +async def test_error_in_errorhandler(sentry_init, capture_events): sentry_init(integrations=[quart_sentry.QuartIntegration()]) + app = quart_app_factory() @app.route("/") async def index(): @@ -358,8 +367,9 @@ async def error_handler(err): @pytest.mark.asyncio -async def test_bad_request_not_captured(sentry_init, capture_events, app): +async def test_bad_request_not_captured(sentry_init, capture_events): sentry_init(integrations=[quart_sentry.QuartIntegration()]) + app = quart_app_factory() events = capture_events() @app.route("/") @@ -374,8 +384,9 @@ async def index(): @pytest.mark.asyncio -async def test_does_not_leak_scope(sentry_init, capture_events, app): +async def test_does_not_leak_scope(sentry_init, capture_events): sentry_init(integrations=[quart_sentry.QuartIntegration()]) + app = quart_app_factory() events = capture_events() sentry_sdk.get_isolation_scope().set_tag("request_data", False) @@ -402,8 +413,9 @@ async def generate(): @pytest.mark.asyncio -async def test_scoped_test_client(sentry_init, app): +async def test_scoped_test_client(sentry_init): sentry_init(integrations=[quart_sentry.QuartIntegration()]) + app = quart_app_factory() @app.route("/") async def index(): @@ -417,12 +429,13 @@ async def index(): @pytest.mark.asyncio @pytest.mark.parametrize("exc_cls", [ZeroDivisionError, Exception]) async def test_errorhandler_for_exception_swallows_exception( - sentry_init, app, capture_events, exc_cls + sentry_init, capture_events, exc_cls ): # In contrast to error handlers for a status code, error # handlers for exceptions can swallow the exception (this is # just how the Quart signal works) sentry_init(integrations=[quart_sentry.QuartIntegration()]) + app = quart_app_factory() events = capture_events() @app.route("/") @@ -441,8 +454,9 @@ async def zerodivision(e): @pytest.mark.asyncio -async def test_tracing_success(sentry_init, capture_events, app): +async def test_tracing_success(sentry_init, capture_events): sentry_init(traces_sample_rate=1.0, integrations=[quart_sentry.QuartIntegration()]) + app = quart_app_factory() @app.before_request async def _(): @@ -474,8 +488,9 @@ async def hi_tx(): @pytest.mark.asyncio -async def test_tracing_error(sentry_init, capture_events, app): +async def test_tracing_error(sentry_init, capture_events): sentry_init(traces_sample_rate=1.0, integrations=[quart_sentry.QuartIntegration()]) + app = quart_app_factory() events = capture_events() @@ -498,8 +513,9 @@ async def error(): @pytest.mark.asyncio -async def test_class_based_views(sentry_init, app, capture_events): +async def test_class_based_views(sentry_init, capture_events): sentry_init(integrations=[quart_sentry.QuartIntegration()]) + app = quart_app_factory() events = capture_events() @app.route("/") @@ -523,12 +539,14 @@ async def dispatch_request(self): @pytest.mark.parametrize("endpoint", ["/sync/thread_ids", "/async/thread_ids"]) +@mock.patch("sentry_sdk.profiler.transaction_profiler.PROFILE_MINIMUM_SAMPLES", 0) @pytest.mark.asyncio -async def test_active_thread_id(sentry_init, capture_envelopes, endpoint, app): +async def test_active_thread_id(sentry_init, capture_envelopes, endpoint): sentry_init( traces_sample_rate=1.0, profiles_sample_rate=1.0, ) + app = quart_app_factory() envelopes = capture_envelopes() @@ -559,12 +577,12 @@ async def test_active_thread_id(sentry_init, capture_envelopes, endpoint, app): @pytest.mark.asyncio -async def test_span_origin(sentry_init, capture_events, app): +async def test_span_origin(sentry_init, capture_events): sentry_init( integrations=[quart_sentry.QuartIntegration()], traces_sample_rate=1.0, ) - + app = quart_app_factory() events = capture_events() client = app.test_client() From d1772a22dfda7fb99f858f055ba95e21dd379c33 Mon Sep 17 00:00:00 2001 From: Tony Xiao Date: Fri, 18 Oct 2024 18:08:51 -0400 Subject: [PATCH 4/4] fix more tests --- tests/integrations/django/asgi/test_asgi.py | 21 ++++---- tests/integrations/quart/test_quart.py | 58 ++++++++++++--------- 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/tests/integrations/django/asgi/test_asgi.py b/tests/integrations/django/asgi/test_asgi.py index 06d14ce093..063aed63ad 100644 --- a/tests/integrations/django/asgi/test_asgi.py +++ b/tests/integrations/django/asgi/test_asgi.py @@ -104,7 +104,9 @@ async def test_async_views(sentry_init, capture_events, application): @pytest.mark.skipif( django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1" ) -async def test_active_thread_id(sentry_init, capture_envelopes, endpoint, application): +async def test_active_thread_id( + sentry_init, capture_envelopes, teardown_profiling, endpoint, application +): with mock.patch( "sentry_sdk.profiler.transaction_profiler.PROFILE_MINIMUM_SAMPLES", 0 ): @@ -121,17 +123,18 @@ async def test_active_thread_id(sentry_init, capture_envelopes, endpoint, applic await comm.wait() assert response["status"] == 200, response["body"] - assert len(envelopes) == 1 - profiles = [item for item in envelopes[0].items if item.type == "profile"] - assert len(profiles) == 1 + assert len(envelopes) == 1 + + profiles = [item for item in envelopes[0].items if item.type == "profile"] + assert len(profiles) == 1 - data = json.loads(response["body"]) + data = json.loads(response["body"]) - for item in profiles: - transactions = item.payload.json["transactions"] - assert len(transactions) == 1 - assert str(data["active"]) == transactions[0]["active_thread_id"] + for item in profiles: + transactions = item.payload.json["transactions"] + assert len(transactions) == 1 + assert str(data["active"]) == transactions[0]["active_thread_id"] transactions = [item for item in envelopes[0].items if item.type == "transaction"] assert len(transactions) == 1 diff --git a/tests/integrations/quart/test_quart.py b/tests/integrations/quart/test_quart.py index 146de7dfcc..f15b968ac5 100644 --- a/tests/integrations/quart/test_quart.py +++ b/tests/integrations/quart/test_quart.py @@ -539,41 +539,47 @@ async def dispatch_request(self): @pytest.mark.parametrize("endpoint", ["/sync/thread_ids", "/async/thread_ids"]) -@mock.patch("sentry_sdk.profiler.transaction_profiler.PROFILE_MINIMUM_SAMPLES", 0) @pytest.mark.asyncio -async def test_active_thread_id(sentry_init, capture_envelopes, endpoint): - sentry_init( - traces_sample_rate=1.0, - profiles_sample_rate=1.0, - ) - app = quart_app_factory() +async def test_active_thread_id( + sentry_init, capture_envelopes, teardown_profiling, endpoint +): + with mock.patch( + "sentry_sdk.profiler.transaction_profiler.PROFILE_MINIMUM_SAMPLES", 0 + ): + sentry_init( + traces_sample_rate=1.0, + profiles_sample_rate=1.0, + ) + app = quart_app_factory() - envelopes = capture_envelopes() + envelopes = capture_envelopes() - async with app.test_client() as client: - response = await client.get(endpoint) - assert response.status_code == 200 + async with app.test_client() as client: + response = await client.get(endpoint) + assert response.status_code == 200 - data = json.loads(await response.get_data(as_text=True)) + data = json.loads(await response.get_data(as_text=True)) - envelopes = [envelope for envelope in envelopes] - assert len(envelopes) == 1 + envelopes = [envelope for envelope in envelopes] + assert len(envelopes) == 1 - profiles = [item for item in envelopes[0].items if item.type == "profile"] - assert len(profiles) == 1, envelopes[0].items + profiles = [item for item in envelopes[0].items if item.type == "profile"] + assert len(profiles) == 1, envelopes[0].items - for item in profiles: - transactions = item.payload.json["transactions"] - assert len(transactions) == 1 - assert str(data["active"]) == transactions[0]["active_thread_id"] + for item in profiles: + transactions = item.payload.json["transactions"] + assert len(transactions) == 1 + assert str(data["active"]) == transactions[0]["active_thread_id"] - transactions = [item for item in envelopes[0].items if item.type == "transaction"] - assert len(transactions) == 1 + transactions = [ + item for item in envelopes[0].items if item.type == "transaction" + ] + assert len(transactions) == 1 - for item in transactions: - transaction = item.payload.json - trace_context = transaction["contexts"]["trace"] - assert str(data["active"]) == trace_context["data"]["thread.id"] + for item in transactions: + transaction = item.payload.json + trace_context = transaction["contexts"]["trace"] + assert str(data["active"]) == trace_context["data"]["thread.id"] @pytest.mark.asyncio