diff --git a/sentry_sdk/integrations/flask.py b/sentry_sdk/integrations/flask.py index f45ec6db20..cb62ddeadb 100644 --- a/sentry_sdk/integrations/flask.py +++ b/sentry_sdk/integrations/flask.py @@ -17,7 +17,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from typing import Any, Callable, Dict, Union + from typing import Any, Callable, Dict, Union, List, Optional from sentry_sdk._types import Event, EventProcessor from sentry_sdk.integrations.wsgi import _ScopedResponse @@ -59,6 +59,7 @@ def __init__( self, transaction_style="endpoint", # type: str http_methods_to_capture=DEFAULT_HTTP_METHODS_TO_CAPTURE, # type: tuple[str, ...] + trace_ignore_status_codes=None, # type: Optional[List[int]] ): # type: (...) -> None if transaction_style not in TRANSACTION_STYLE_VALUES: @@ -68,6 +69,7 @@ def __init__( ) self.transaction_style = transaction_style self.http_methods_to_capture = tuple(map(str.upper, http_methods_to_capture)) + self.trace_ignore_status_codes = trace_ignore_status_codes or [] @staticmethod def setup_once(): @@ -95,10 +97,9 @@ def setup_once(): def sentry_patched_wsgi_app(self, environ, start_response): # type: (Any, Dict[str, str], Callable[..., Any]) -> _ScopedResponse - if sentry_sdk.get_client().get_integration(FlaskIntegration) is None: - return old_app(self, environ, start_response) - integration = sentry_sdk.get_client().get_integration(FlaskIntegration) + if integration is None: + return old_app(self, environ, start_response) middleware = SentryWsgiMiddleware( lambda *a, **kw: old_app(self, *a, **kw), @@ -108,6 +109,7 @@ def sentry_patched_wsgi_app(self, environ, start_response): if integration else DEFAULT_HTTP_METHODS_TO_CAPTURE ), + trace_ignore_status_codes=integration.trace_ignore_status_codes, ) return middleware(environ, start_response) diff --git a/sentry_sdk/integrations/wsgi.py b/sentry_sdk/integrations/wsgi.py index e628e50e69..00e5668ed3 100644 --- a/sentry_sdk/integrations/wsgi.py +++ b/sentry_sdk/integrations/wsgi.py @@ -32,6 +32,7 @@ from typing import Optional from typing import TypeVar from typing import Protocol + from typing import List from sentry_sdk.utils import ExcInfo from sentry_sdk._types import Event, EventProcessor @@ -75,6 +76,7 @@ class SentryWsgiMiddleware: "use_x_forwarded_for", "span_origin", "http_methods_to_capture", + "trace_ignore_status_codes", ) def __init__( @@ -83,12 +85,14 @@ def __init__( use_x_forwarded_for=False, # type: bool span_origin="manual", # type: str http_methods_to_capture=DEFAULT_HTTP_METHODS_TO_CAPTURE, # type: Tuple[str, ...] + trace_ignore_status_codes=None, # type: Optional[List[int]] ): # type: (...) -> None self.app = app self.use_x_forwarded_for = use_x_forwarded_for self.span_origin = span_origin self.http_methods_to_capture = http_methods_to_capture + self.trace_ignore_status_codes = trace_ignore_status_codes or [] def __call__(self, environ, start_response): # type: (Dict[str, str], Callable[..., Any]) -> _ScopedResponse @@ -131,7 +135,10 @@ def __call__(self, environ, start_response): response = self.app( environ, partial( - _sentry_start_response, start_response, transaction + _sentry_start_response, + start_response, + transaction, + self.trace_ignore_status_codes, ), ) except BaseException: @@ -145,6 +152,7 @@ def __call__(self, environ, start_response): def _sentry_start_response( # type: ignore old_start_response, # type: StartResponse transaction, # type: Optional[Transaction] + trace_ignore_status_codes, # type: List[int] status, # type: str response_headers, # type: WsgiResponseHeaders exc_info=None, # type: Optional[WsgiExcInfo] @@ -152,6 +160,8 @@ def _sentry_start_response( # type: ignore # type: (...) -> WsgiResponseIter with capture_internal_exceptions(): status_int = int(status.split(" ", 1)[0]) + if transaction is not None and status_int in trace_ignore_status_codes: + transaction.sampled = False if transaction is not None: transaction.set_http_status(status_int) diff --git a/tests/integrations/flask/test_flask.py b/tests/integrations/flask/test_flask.py index 49ee684797..947cfcf04e 100644 --- a/tests/integrations/flask/test_flask.py +++ b/tests/integrations/flask/test_flask.py @@ -809,6 +809,58 @@ def error(): assert exception["type"] == "ZeroDivisionError" +def test_tracing_not_found(sentry_init, capture_events, app): + sentry_init(traces_sample_rate=1.0, integrations=[flask_sentry.FlaskIntegration()]) + + @app.before_request + def _(): + set_tag("before_request", "yes") + + @app.route("/message_tx") + def hi_tx(): + set_tag("view", "yes") + capture_message("hi") + return "ok" + + events = capture_events() + + with app.test_client() as client: + response = client.get("/wrong_route") + assert response.status_code == 404 + + (transaction_event,) = events + + assert transaction_event["type"] == "transaction" + assert transaction_event["transaction"] == "generic WSGI request" + assert transaction_event["contexts"]["trace"]["status"] == "not_found" + assert transaction_event["tags"]["before_request"] == "yes" + + +def test_tracing_ignore_not_found(sentry_init, capture_events, app): + sentry_init( + traces_sample_rate=1.0, + integrations=[flask_sentry.FlaskIntegration(trace_ignore_status_codes=[404])], + ) + + @app.before_request + def _(): + set_tag("before_request", "yes") + + @app.route("/message_tx") + def hi_tx(): + set_tag("view", "yes") + capture_message("hi") + return "ok" + + events = capture_events() + + with app.test_client() as client: + response = client.get("/wrong_route") + assert response.status_code == 404 + + assert not events + + def test_error_has_trace_context_if_tracing_disabled(sentry_init, capture_events, app): sentry_init(integrations=[flask_sentry.FlaskIntegration()])