From e51cbe9dbbe7f358ef019965fb61e19907318d40 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 11 Jul 2022 15:08:15 +0200 Subject: [PATCH 01/45] Added transaction source to flask --- .pre-commit-config.yaml | 6 ++-- sentry_sdk/integrations/flask.py | 60 ++++++++++++++++++-------------- sentry_sdk/utils.py | 10 ++++++ 3 files changed, 47 insertions(+), 29 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 753558186f..3f7e548518 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,18 +2,18 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 + rev: v4.3.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - repo: https://github.com/psf/black - rev: stable + rev: 22.6.0 hooks: - id: black - repo: https://gitlab.com/pycqa/flake8 - rev: 4.0.1 + rev: 3.9.2 hooks: - id: flake8 diff --git a/sentry_sdk/integrations/flask.py b/sentry_sdk/integrations/flask.py index 5aade50a94..e786a78955 100644 --- a/sentry_sdk/integrations/flask.py +++ b/sentry_sdk/integrations/flask.py @@ -1,23 +1,23 @@ from __future__ import absolute_import +from sentry_sdk._types import MYPY from sentry_sdk.hub import Hub, _should_send_default_pii -from sentry_sdk.utils import capture_internal_exceptions, event_from_exception -from sentry_sdk.integrations import Integration, DidNotEnable -from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware +from sentry_sdk.integrations import DidNotEnable, Integration from sentry_sdk.integrations._wsgi_common import RequestExtractor - -from sentry_sdk._types import MYPY +from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware +from sentry_sdk.utils import ( + TRANSACTION_SOURCE_COMPONENT, + TRANSACTION_SOURCE_ROUTE, + capture_internal_exceptions, + event_from_exception, +) if MYPY: - from sentry_sdk.integrations.wsgi import _ScopedResponse - from typing import Any - from typing import Dict - from werkzeug.datastructures import ImmutableMultiDict - from werkzeug.datastructures import FileStorage - from typing import Union - from typing import Callable + from typing import Any, Callable, Dict, Union from sentry_sdk._types import EventProcessor + from sentry_sdk.integrations.wsgi import _ScopedResponse + from werkzeug.datastructures import FileStorage, ImmutableMultiDict try: @@ -26,14 +26,9 @@ flask_login = None try: - from flask import ( # type: ignore - Markup, - Request, - Flask, - _request_ctx_stack, - _app_ctx_stack, - __version__ as FLASK_VERSION, - ) + from flask import Flask, Markup, Request + from flask import __version__ as FLASK_VERSION # type: ignore + from flask import _app_ctx_stack, _request_ctx_stack from flask.signals import ( before_render_template, got_request_exception, @@ -114,6 +109,22 @@ def _add_sentry_trace(sender, template, context, **extra): ) +def _add_transaction(scope, style, request): + name_for_style = { + "url": request.url_rule.rule, + "endpoint": request.url_rule.endpoint, + } + source_for_style = { + "url": TRANSACTION_SOURCE_ROUTE, + "endpoint": TRANSACTION_SOURCE_COMPONENT, + } + + scope.transaction = name_for_style[style] + scope.transaction_info = {"source": source_for_style[style]} + + return scope + + def _request_started(sender, **kwargs): # type: (Flask, **Any) -> None hub = Hub.current @@ -125,13 +136,10 @@ def _request_started(sender, **kwargs): with hub.configure_scope() as scope: request = _request_ctx_stack.top.request - # Set the transaction name here, but rely on WSGI middleware to actually - # start the transaction + # Set the transaction name and source here, + # but rely on WSGI middleware to actually start the transaction try: - if integration.transaction_style == "endpoint": - scope.transaction = request.url_rule.endpoint - elif integration.transaction_style == "url": - scope.transaction = request.url_rule.rule + scope = _add_transaction(scope, integration.transaction_style, request) except Exception: pass diff --git a/sentry_sdk/utils.py b/sentry_sdk/utils.py index 38ba4d7857..9f49738d1c 100644 --- a/sentry_sdk/utils.py +++ b/sentry_sdk/utils.py @@ -42,6 +42,16 @@ MAX_STRING_LENGTH = 512 BASE64_ALPHABET = re.compile(r"^[a-zA-Z0-9/+=]*$") +# Transaction source +# see https://develop.sentry.dev/sdk/event-payloads/transaction/#transaction-annotations +TRANSACTION_SOURCE_CUSTOM = "custom" +TRANSACTION_SOURCE_URL = "url" +TRANSACTION_SOURCE_ROUTE = "route" +TRANSACTION_SOURCE_VIEW = "view" +TRANSACTION_SOURCE_COMPONENT = "component" +TRANSACTION_SOURCE_TASK = "task" +TRANSACTION_SOURCE_UNKNOWN = "unknown" + def json_dumps(data): # type: (Any) -> bytes From 9cfe349f80ba96a587a339021448084a3a1e7a7f Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 11 Jul 2022 15:34:34 +0200 Subject: [PATCH 02/45] Added transaction info to scope (and events) --- sentry_sdk/integrations/flask.py | 7 +++++-- sentry_sdk/scope.py | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/integrations/flask.py b/sentry_sdk/integrations/flask.py index e786a78955..e96f0437da 100644 --- a/sentry_sdk/integrations/flask.py +++ b/sentry_sdk/integrations/flask.py @@ -5,6 +5,7 @@ from sentry_sdk.integrations import DidNotEnable, Integration from sentry_sdk.integrations._wsgi_common import RequestExtractor from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware +from sentry_sdk.scope import Scope from sentry_sdk.utils import ( TRANSACTION_SOURCE_COMPONENT, TRANSACTION_SOURCE_ROUTE, @@ -110,6 +111,7 @@ def _add_sentry_trace(sender, template, context, **extra): def _add_transaction(scope, style, request): + # type: (Scope, str, Request) -> Scope name_for_style = { "url": request.url_rule.rule, "endpoint": request.url_rule.endpoint, @@ -119,8 +121,9 @@ def _add_transaction(scope, style, request): "endpoint": TRANSACTION_SOURCE_COMPONENT, } - scope.transaction = name_for_style[style] - scope.transaction_info = {"source": source_for_style[style]} + scope.set_transaction_name( + name_for_style.get(style), source=source_for_style.get(style) + ) return scope diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index bcfbf5c166..a612132a42 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -81,6 +81,7 @@ class Scope(object): # note that for legacy reasons, _transaction is the transaction *name*, # not a Transaction object (the object is stored in _span) "_transaction", + "_transaction_info", "_user", "_tags", "_contexts", @@ -109,6 +110,7 @@ def clear(self): self._level = None # type: Optional[str] self._fingerprint = None # type: Optional[List[str]] self._transaction = None # type: Optional[str] + self._transaction_info = {} # type: Dict[str, str] self._user = None # type: Optional[Dict[str, Any]] self._tags = {} # type: Dict[str, Any] @@ -176,6 +178,17 @@ def transaction(self, value): if self._span and self._span.containing_transaction: self._span.containing_transaction.name = value + def set_transaction_name(self, name, source=None): + # type: (str, Optional[str]) -> None + """Set the transaction name and optionally the transaction source.""" + self._transaction = name + + if self._span and self._span.containing_transaction: + self._span.containing_transaction.name = name + + if source: + self._transaction_info["source"] = source + @_attr_setter def user(self, value): # type: (Optional[Dict[str, Any]]) -> None @@ -363,6 +376,9 @@ def _drop(event, cause, ty): if event.get("transaction") is None and self._transaction is not None: event["transaction"] = self._transaction + if event.get("transaction_info") is None and self._transaction_info is not None: + event["transaction_info"] = self._transaction_info + if event.get("fingerprint") is None and self._fingerprint is not None: event["fingerprint"] = self._fingerprint @@ -406,6 +422,8 @@ def update_from_scope(self, scope): self._fingerprint = scope._fingerprint if scope._transaction is not None: self._transaction = scope._transaction + if scope._transaction_info is not None: + self._transaction_info.update(scope._transaction_info) if scope._user is not None: self._user = scope._user if scope._tags: @@ -452,6 +470,7 @@ def __copy__(self): rv._name = self._name rv._fingerprint = self._fingerprint rv._transaction = self._transaction + rv._transaction_info = dict(self._transaction_info) rv._user = self._user rv._tags = dict(self._tags) From 3ac05b4039af6062ef12199655f5b21941e6f936 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 11 Jul 2022 16:03:48 +0200 Subject: [PATCH 03/45] Deprecated old transaction setter --- sentry_sdk/scope.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index a612132a42..6f3909ecbd 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -164,7 +164,10 @@ def transaction(self): def transaction(self, value): # type: (Any) -> None # would be type: (Optional[str]) -> None, see https://github.com/python/mypy/issues/3004 - """When set this forces a specific transaction name to be set.""" + """When set this forces a specific transaction name to be set. + + Deprecated: use set_transaction_name instead.""" + # XXX: the docstring above is misleading. The implementation of # apply_to_event prefers an existing value of event.transaction over # anything set in the scope. @@ -174,6 +177,10 @@ def transaction(self, value): # Without breaking version compatibility, we could make the setter set a # transaction name or transaction (self._span) depending on the type of # the value argument. + + logger.warning( + "Assigning to scope.transaction directly is deprecated: use scope.set_transaction_name() instead." + ) self._transaction = value if self._span and self._span.containing_transaction: self._span.containing_transaction.name = value From 13f8075c015a32d168c41706e7881c782544be23 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 11 Jul 2022 19:23:52 +0200 Subject: [PATCH 04/45] Fixed linting --- sentry_sdk/integrations/flask.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/integrations/flask.py b/sentry_sdk/integrations/flask.py index e96f0437da..670917a0fd 100644 --- a/sentry_sdk/integrations/flask.py +++ b/sentry_sdk/integrations/flask.py @@ -27,9 +27,9 @@ flask_login = None try: - from flask import Flask, Markup, Request + from flask import Flask, Markup, Request # type: ignore from flask import __version__ as FLASK_VERSION # type: ignore - from flask import _app_ctx_stack, _request_ctx_stack + from flask import _app_ctx_stack, _request_ctx_stack # type: ignore from flask.signals import ( before_render_template, got_request_exception, From b7be7bfa16706a5fbeef0ce2cdb0df4ce775ced8 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 11 Jul 2022 19:45:19 +0200 Subject: [PATCH 05/45] Added tests for transaction name and source in Flask. --- tests/integrations/flask/test_flask.py | 52 +++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/tests/integrations/flask/test_flask.py b/tests/integrations/flask/test_flask.py index 8723a35c86..5076c72b93 100644 --- a/tests/integrations/flask/test_flask.py +++ b/tests/integrations/flask/test_flask.py @@ -46,6 +46,11 @@ def hi(): capture_message("hi") return "ok" + @app.route("/message/") + def hi_with_id(message_id): + capture_message("hi again") + return "ok" + return app @@ -74,7 +79,8 @@ def test_has_context(sentry_init, app, capture_events): @pytest.mark.parametrize( - "transaction_style,expected_transaction", [("endpoint", "hi"), ("url", "/message")] + "transaction_style,expected_transaction", + [("endpoint", "hi"), ("url", "/message")], ) def test_transaction_style( sentry_init, app, capture_events, transaction_style, expected_transaction @@ -94,6 +100,50 @@ def test_transaction_style( assert event["transaction"] == expected_transaction +@pytest.mark.parametrize( + "transaction_style,expected_name", + [("endpoint", "hi_with_id"), ("url", "/message/")], +) +def test_transaction_name( + sentry_init, app, capture_events, transaction_style, expected_name +): + sentry_init( + integrations=[ + flask_sentry.FlaskIntegration(transaction_style=transaction_style) + ] + ) + events = capture_events() + + client = app.test_client() + response = client.get("/message/123567") + assert response.status_code == 200 + + (event,) = events + assert event["transaction"] == expected_name + + +@pytest.mark.parametrize( + "transaction_style,expected_source", + [("endpoint", "component"), ("url", "route")], +) +def test_transaction_source( + sentry_init, app, capture_events, transaction_style, expected_source +): + sentry_init( + integrations=[ + flask_sentry.FlaskIntegration(transaction_style=transaction_style) + ] + ) + events = capture_events() + + client = app.test_client() + response = client.get("/message") + assert response.status_code == 200 + + (event,) = events + assert event["transaction_info"] == {"source": expected_source} + + @pytest.mark.parametrize("debug", (True, False)) @pytest.mark.parametrize("testing", (True, False)) def test_errors( From 7185bfd427e7e9433ef26af8166bdcfecd516de5 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 11 Jul 2022 19:57:16 +0200 Subject: [PATCH 06/45] Renamed function for clarity --- sentry_sdk/integrations/flask.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/integrations/flask.py b/sentry_sdk/integrations/flask.py index 670917a0fd..bf58d7e610 100644 --- a/sentry_sdk/integrations/flask.py +++ b/sentry_sdk/integrations/flask.py @@ -110,7 +110,7 @@ def _add_sentry_trace(sender, template, context, **extra): ) -def _add_transaction(scope, style, request): +def _set_transaction_name_and_source(scope, style, request): # type: (Scope, str, Request) -> Scope name_for_style = { "url": request.url_rule.rule, @@ -142,7 +142,9 @@ def _request_started(sender, **kwargs): # Set the transaction name and source here, # but rely on WSGI middleware to actually start the transaction try: - scope = _add_transaction(scope, integration.transaction_style, request) + scope = _set_transaction_name_and_source( + scope, integration.transaction_style, request + ) except Exception: pass From 1fa42c141ee092d1125aef85db2906dfc0cdc5ef Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 11 Jul 2022 20:09:03 +0200 Subject: [PATCH 07/45] Set transaction source in ASGI. --- sentry_sdk/integrations/asgi.py | 54 ++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/sentry_sdk/integrations/asgi.py b/sentry_sdk/integrations/asgi.py index 5f7810732b..75366df9a6 100644 --- a/sentry_sdk/integrations/asgi.py +++ b/sentry_sdk/integrations/asgi.py @@ -14,6 +14,8 @@ from sentry_sdk.integrations._wsgi_common import _filter_headers from sentry_sdk.sessions import auto_session_tracking from sentry_sdk.utils import ( + TRANSACTION_SOURCE_COMPONENT, + TRANSACTION_SOURCE_ROUTE, ContextVar, event_from_exception, transaction_from_function, @@ -183,25 +185,14 @@ def event_processor(self, event, hint, asgi_scope): if client and _should_send_default_pii(): request_info["env"] = {"REMOTE_ADDR": self._get_ip(asgi_scope)} - if ( + has_default_transaction_name = ( event.get("transaction", _DEFAULT_TRANSACTION_NAME) == _DEFAULT_TRANSACTION_NAME - ): - if self.transaction_style == "endpoint": - endpoint = asgi_scope.get("endpoint") - # Webframeworks like Starlette mutate the ASGI env once routing is - # done, which is sometime after the request has started. If we have - # an endpoint, overwrite our generic transaction name. - if endpoint: - event["transaction"] = transaction_from_function(endpoint) - elif self.transaction_style == "url": - # FastAPI includes the route object in the scope to let Sentry extract the - # path from it for the transaction name - route = asgi_scope.get("route") - if route: - path = getattr(route, "path", None) - if path is not None: - event["transaction"] = path + ) + if has_default_transaction_name: + event = self._set_transaction_name_and_source( + event, self.transaction_style, asgi_scope + ) event["request"] = request_info @@ -213,6 +204,35 @@ def event_processor(self, event, hint, asgi_scope): # data to your liking it's recommended to use the `before_send` callback # for that. + def _set_transaction_name_and_source(event, transaction_style, asgi_scope): + # type: (Event, str, Any) -> Event + + if transaction_style == "endpoint": + endpoint = asgi_scope.get("endpoint") + # Webframeworks like Starlette mutate the ASGI env once routing is + # done, which is sometime after the request has started. If we have + # an endpoint, overwrite our generic transaction name. + if endpoint: + event["transaction"] = transaction_from_function(endpoint) + + elif transaction_style == "url": + # FastAPI includes the route object in the scope to let Sentry extract the + # path from it for the transaction name + route = asgi_scope.get("route") + if route: + path = getattr(route, "path", None) + if path is not None: + event["transaction"] = path + + # Set transaction source + source_for_style = { + "url": TRANSACTION_SOURCE_ROUTE, + "endpoint": TRANSACTION_SOURCE_COMPONENT, + } + event["transaction_info"] = {"source": source_for_style[transaction_style]} + + return event + def _get_url(self, scope, default_scheme, host): # type: (Dict[str, Any], Literal["ws", "http"], Optional[str]) -> str """ From cc3a32bbe6d8a08c8f7c34ac0d43b793b1dc9d9a Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 11 Jul 2022 20:53:52 +0200 Subject: [PATCH 08/45] Added tests for transaction name and source in ASGI --- sentry_sdk/integrations/asgi.py | 31 ++++++++++++++++------ tests/integrations/asgi/test_asgi.py | 39 ++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/sentry_sdk/integrations/asgi.py b/sentry_sdk/integrations/asgi.py index 75366df9a6..2c03b7e61a 100644 --- a/sentry_sdk/integrations/asgi.py +++ b/sentry_sdk/integrations/asgi.py @@ -16,6 +16,7 @@ from sentry_sdk.utils import ( TRANSACTION_SOURCE_COMPONENT, TRANSACTION_SOURCE_ROUTE, + TRANSACTION_SOURCE_UNKNOWN, ContextVar, event_from_exception, transaction_from_function, @@ -38,6 +39,7 @@ _asgi_middleware_applied = ContextVar("sentry_asgi_middleware_applied") _DEFAULT_TRANSACTION_NAME = "generic ASGI request" +_DEFAULT_TRANSACTION_SOURCE = TRANSACTION_SOURCE_UNKNOWN TRANSACTION_STYLE_VALUES = ("endpoint", "url") @@ -185,14 +187,9 @@ def event_processor(self, event, hint, asgi_scope): if client and _should_send_default_pii(): request_info["env"] = {"REMOTE_ADDR": self._get_ip(asgi_scope)} - has_default_transaction_name = ( - event.get("transaction", _DEFAULT_TRANSACTION_NAME) - == _DEFAULT_TRANSACTION_NAME + event = self._set_transaction_name_and_source( + event, self.transaction_style, asgi_scope ) - if has_default_transaction_name: - event = self._set_transaction_name_and_source( - event, self.transaction_style, asgi_scope - ) event["request"] = request_info @@ -204,9 +201,16 @@ def event_processor(self, event, hint, asgi_scope): # data to your liking it's recommended to use the `before_send` callback # for that. - def _set_transaction_name_and_source(event, transaction_style, asgi_scope): + def _set_transaction_name_and_source(self, event, transaction_style, asgi_scope): # type: (Event, str, Any) -> Event + transaction_name_already_set = ( + event.get("transaction", _DEFAULT_TRANSACTION_NAME) + != _DEFAULT_TRANSACTION_NAME + ) + if transaction_name_already_set: + return + if transaction_style == "endpoint": endpoint = asgi_scope.get("endpoint") # Webframeworks like Starlette mutate the ASGI env once routing is @@ -231,6 +235,17 @@ def _set_transaction_name_and_source(event, transaction_style, asgi_scope): } event["transaction_info"] = {"source": source_for_style[transaction_style]} + transaction_name_not_set = ( + event.get("transaction", _DEFAULT_TRANSACTION_NAME) + == _DEFAULT_TRANSACTION_NAME + ) + + if transaction_name_not_set: + # If the setting of the transaction name did not work + # then set an unknown source. This can happen when ASGI frameworks + # that are not yet supported well are used. + event["transaction_info"] = {"source": TRANSACTION_SOURCE_UNKNOWN} + return event def _get_url(self, scope, default_scheme, host): diff --git a/tests/integrations/asgi/test_asgi.py b/tests/integrations/asgi/test_asgi.py index 5383b1a308..edcf591b9d 100644 --- a/tests/integrations/asgi/test_asgi.py +++ b/tests/integrations/asgi/test_asgi.py @@ -230,6 +230,45 @@ def kangaroo_handler(request): ) +@pytest.mark.parametrize( + "transaction_style,expected_name,expected_source", + [ + ( + "endpoint", + "tests.integrations.asgi.test_asgi.test_transaction_name_and_source..hi_with_id", + "component", + ), + ( + "url", + "generic ASGI request", # the AsgiMiddleware can not extract routes from the Starlette framework used here for testing. + "unknown", + ), + ], +) +def test_transaction_name_and_source( + sentry_init, app, capture_events, transaction_style, expected_name, expected_source +): + sentry_init(send_default_pii=True) + + custom_app = Starlette() + + @custom_app.route("/sync-message-with-id/{user_id:int}") + def hi_with_id(request): + capture_message("hi", level="error") + return PlainTextResponse("ok") + + custom_app = SentryAsgiMiddleware(custom_app, transaction_style=transaction_style) + + events = capture_events() + + client = TestClient(custom_app) + client.get("/sync-message-with-id/123") + + (event,) = events + assert event["transaction"] == expected_name + assert event["transaction_info"] == {"source": expected_source} + + def test_traces_sampler_gets_scope_in_sampling_context( app, sentry_init, DictionaryContaining # noqa: N803 ): From 5cf698d547b3608d97bb12c1e12438cffc3d2605 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 12 Jul 2022 09:43:55 +0200 Subject: [PATCH 09/45] Added transaction source to bottle --- sentry_sdk/integrations/bottle.py | 39 +++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/sentry_sdk/integrations/bottle.py b/sentry_sdk/integrations/bottle.py index 4fa077e8f6..828b087cca 100644 --- a/sentry_sdk/integrations/bottle.py +++ b/sentry_sdk/integrations/bottle.py @@ -2,6 +2,8 @@ from sentry_sdk.hub import Hub from sentry_sdk.utils import ( + TRANSACTION_SOURCE_COMPONENT, + TRANSACTION_SOURCE_ROUTE, capture_internal_exceptions, event_from_exception, transaction_from_function, @@ -20,7 +22,7 @@ from typing import Optional from bottle import FileUpload, FormsDict, LocalRequest # type: ignore - from sentry_sdk._types import EventProcessor + from sentry_sdk._types import EventProcessor, Event try: from bottle import ( @@ -176,24 +178,37 @@ def size_of_file(self, file): return file.content_length +def _set_transaction_name_and_source(event, transaction_style, request): + # type: (Event, str, Any) -> Event + + name_for_style = { + "url": request.route.rule, + "endpoint": request.route.name + or transaction_from_function(request.route.callback), + } + source_for_style = { + "url": TRANSACTION_SOURCE_ROUTE, + "endpoint": TRANSACTION_SOURCE_COMPONENT, + } + + event["transaction"] = name_for_style[transaction_style] + event["transaction_info"] = {"source": source_for_style[transaction_style]} + + return event + + def _make_request_event_processor(app, request, integration): # type: (Bottle, LocalRequest, BottleIntegration) -> EventProcessor - def inner(event, hint): + def event_processor(event, hint): # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any] - try: - if integration.transaction_style == "endpoint": - event["transaction"] = request.route.name or transaction_from_function( - request.route.callback - ) - elif integration.transaction_style == "url": - event["transaction"] = request.route.rule - except Exception: - pass + event = _set_transaction_name_and_source( + event, integration.transaction_style, request + ) with capture_internal_exceptions(): BottleRequestExtractor(request).extract_into_event(event) return event - return inner + return event_processor From 3b9487514e6250297caa4801a32705c22fe6c274 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 12 Jul 2022 09:44:39 +0200 Subject: [PATCH 10/45] Improved tests for transaction info for bottle --- tests/integrations/bottle/test_bottle.py | 25 +++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/tests/integrations/bottle/test_bottle.py b/tests/integrations/bottle/test_bottle.py index ec133e4d75..4d4ad08dd5 100644 --- a/tests/integrations/bottle/test_bottle.py +++ b/tests/integrations/bottle/test_bottle.py @@ -24,6 +24,11 @@ def hi(): capture_message("hi") return "ok" + @app.route("/message/") + def hi_with_id(message_id): + capture_message("hi") + return "ok" + @app.route("/message-named-route", name="hi") def named_hi(): capture_message("hi") @@ -55,20 +60,21 @@ def test_has_context(sentry_init, app, capture_events, get_client): @pytest.mark.parametrize( - "url,transaction_style,expected_transaction", + "url,transaction_style,expected_transaction,expected_source", [ - ("/message", "endpoint", "hi"), - ("/message", "url", "/message"), - ("/message-named-route", "endpoint", "hi"), + ("/message", "endpoint", "test_bottle.app..hi", "component"), + ("/message", "url", "/message", "route"), + ("/message/123456", "url", "/message/", "route"), + ("/message-named-route", "endpoint", "hi", "component"), ], ) def test_transaction_style( sentry_init, - app, - capture_events, + url, transaction_style, expected_transaction, - url, + expected_source, + capture_events, get_client, ): sentry_init( @@ -79,11 +85,12 @@ def test_transaction_style( events = capture_events() client = get_client() - response = client.get("/message") + response = client.get(url) assert response[1] == "200 OK" (event,) = events - assert event["transaction"].endswith(expected_transaction) + assert event["transaction"] == expected_transaction + assert event["transaction_info"] == {"source": expected_source} @pytest.mark.parametrize("debug", (True, False), ids=["debug", "nodebug"]) From 0fcf26e611d5dc727baf894319ff37b3cbb269f0 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 12 Jul 2022 09:49:46 +0200 Subject: [PATCH 11/45] Refactored tests for transaction info in Flask. --- tests/integrations/flask/test_flask.py | 62 +++++++------------------- 1 file changed, 15 insertions(+), 47 deletions(-) diff --git a/tests/integrations/flask/test_flask.py b/tests/integrations/flask/test_flask.py index 5076c72b93..d64e616b37 100644 --- a/tests/integrations/flask/test_flask.py +++ b/tests/integrations/flask/test_flask.py @@ -79,11 +79,22 @@ def test_has_context(sentry_init, app, capture_events): @pytest.mark.parametrize( - "transaction_style,expected_transaction", - [("endpoint", "hi"), ("url", "/message")], + "url,transaction_style,expected_transaction,expected_source", + [ + ("/message", "endpoint", "hi", "component"), + ("/message", "url", "/message", "route"), + ("/message/123456", "endpoint", "hi_with_id", "component"), + ("/message/123456", "url", "/message/", "route"), + ], ) def test_transaction_style( - sentry_init, app, capture_events, transaction_style, expected_transaction + sentry_init, + app, + capture_events, + url, + transaction_style, + expected_transaction, + expected_source, ): sentry_init( integrations=[ @@ -93,54 +104,11 @@ def test_transaction_style( events = capture_events() client = app.test_client() - response = client.get("/message") + response = client.get(url) assert response.status_code == 200 (event,) = events assert event["transaction"] == expected_transaction - - -@pytest.mark.parametrize( - "transaction_style,expected_name", - [("endpoint", "hi_with_id"), ("url", "/message/")], -) -def test_transaction_name( - sentry_init, app, capture_events, transaction_style, expected_name -): - sentry_init( - integrations=[ - flask_sentry.FlaskIntegration(transaction_style=transaction_style) - ] - ) - events = capture_events() - - client = app.test_client() - response = client.get("/message/123567") - assert response.status_code == 200 - - (event,) = events - assert event["transaction"] == expected_name - - -@pytest.mark.parametrize( - "transaction_style,expected_source", - [("endpoint", "component"), ("url", "route")], -) -def test_transaction_source( - sentry_init, app, capture_events, transaction_style, expected_source -): - sentry_init( - integrations=[ - flask_sentry.FlaskIntegration(transaction_style=transaction_style) - ] - ) - events = capture_events() - - client = app.test_client() - response = client.get("/message") - assert response.status_code == 200 - - (event,) = events assert event["transaction_info"] == {"source": expected_source} From c55b991cdf3207a471ec1444709f876873c556d6 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 12 Jul 2022 10:14:17 +0200 Subject: [PATCH 12/45] Refactored tests for transaction info for ASGI. --- tests/integrations/asgi/test_asgi.py | 84 +++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 15 deletions(-) diff --git a/tests/integrations/asgi/test_asgi.py b/tests/integrations/asgi/test_asgi.py index edcf591b9d..aed2157612 100644 --- a/tests/integrations/asgi/test_asgi.py +++ b/tests/integrations/asgi/test_asgi.py @@ -35,6 +35,33 @@ async def hi2(request): return app +@pytest.fixture +def transaction_app(): + transaction_app = Starlette() + + @transaction_app.route("/sync-message") + def hi(request): + capture_message("hi", level="error") + return PlainTextResponse("ok") + + @transaction_app.route("/sync-message/{user_id:int}") + def hi_with_id(request): + capture_message("hi", level="error") + return PlainTextResponse("ok") + + @transaction_app.route("/async-message") + async def async_hi(request): + capture_message("hi", level="error") + return PlainTextResponse("ok") + + @transaction_app.route("/async-message/{user_id:int}") + async def async_hi_with_id(request): + capture_message("hi", level="error") + return PlainTextResponse("ok") + + return transaction_app + + @pytest.mark.skipif(sys.version_info < (3, 7), reason="requires python3.7 or higher") def test_sync_request_data(sentry_init, app, capture_events): sentry_init(send_default_pii=True) @@ -231,41 +258,68 @@ def kangaroo_handler(request): @pytest.mark.parametrize( - "transaction_style,expected_name,expected_source", + "url,transaction_style,expected_transaction,expected_source", [ ( + "/sync-message", + "endpoint", + "tests.integrations.asgi.test_asgi.transaction_app..hi", + "component", + ), + ( + "/sync-message", + "url", + "generic ASGI request", # the AsgiMiddleware can not extract routes from the Starlette framework used here for testing. + "unknown", + ), + ( + "/sync-message/123456", "endpoint", - "tests.integrations.asgi.test_asgi.test_transaction_name_and_source..hi_with_id", + "tests.integrations.asgi.test_asgi.transaction_app..hi_with_id", "component", ), ( + "/sync-message/123456", + "url", + "generic ASGI request", # the AsgiMiddleware can not extract routes from the Starlette framework used here for testing. + "unknown", + ), + ( + "/async-message", + "endpoint", + "tests.integrations.asgi.test_asgi.transaction_app..async_hi", + "component", + ), + ( + "/async-message", "url", "generic ASGI request", # the AsgiMiddleware can not extract routes from the Starlette framework used here for testing. "unknown", ), ], ) -def test_transaction_name_and_source( - sentry_init, app, capture_events, transaction_style, expected_name, expected_source +def test_transaction_style( + sentry_init, + transaction_app, + url, + transaction_style, + expected_transaction, + expected_source, + capture_events, ): sentry_init(send_default_pii=True) - custom_app = Starlette() - - @custom_app.route("/sync-message-with-id/{user_id:int}") - def hi_with_id(request): - capture_message("hi", level="error") - return PlainTextResponse("ok") - - custom_app = SentryAsgiMiddleware(custom_app, transaction_style=transaction_style) + transaction_app = SentryAsgiMiddleware( + transaction_app, transaction_style=transaction_style + ) events = capture_events() - client = TestClient(custom_app) - client.get("/sync-message-with-id/123") + client = TestClient(transaction_app) + client.get(url) (event,) = events - assert event["transaction"] == expected_name + assert event["transaction"] == expected_transaction assert event["transaction_info"] == {"source": expected_source} From d4792fd6bbc4e23627d88f69ca3d9429573544a6 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 11:54:40 +0200 Subject: [PATCH 13/45] Cleanup Flask --- sentry_sdk/integrations/flask.py | 40 ++++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/sentry_sdk/integrations/flask.py b/sentry_sdk/integrations/flask.py index bf58d7e610..f15dcc02f1 100644 --- a/sentry_sdk/integrations/flask.py +++ b/sentry_sdk/integrations/flask.py @@ -110,20 +110,24 @@ def _add_sentry_trace(sender, template, context, **extra): ) -def _set_transaction_name_and_source(scope, style, request): +def _set_transaction_name_and_source(scope, transaction_style, request): # type: (Scope, str, Request) -> Scope - name_for_style = { - "url": request.url_rule.rule, - "endpoint": request.url_rule.endpoint, - } - source_for_style = { - "url": TRANSACTION_SOURCE_ROUTE, - "endpoint": TRANSACTION_SOURCE_COMPONENT, - } - - scope.set_transaction_name( - name_for_style.get(style), source=source_for_style.get(style) - ) + try: + name_for_style = { + "url": request.url_rule.rule, + "endpoint": request.url_rule.endpoint, + } + source_for_style = { + "url": TRANSACTION_SOURCE_ROUTE, + "endpoint": TRANSACTION_SOURCE_COMPONENT, + } + + scope.set_transaction_name( + name_for_style.get(transaction_style), + source=source_for_style.get(transaction_style), + ) + except Exception: + pass return scope @@ -141,13 +145,9 @@ def _request_started(sender, **kwargs): # Set the transaction name and source here, # but rely on WSGI middleware to actually start the transaction - try: - scope = _set_transaction_name_and_source( - scope, integration.transaction_style, request - ) - except Exception: - pass - + scope = _set_transaction_name_and_source( + scope, integration.transaction_style, request + ) evt_processor = _make_request_event_processor(app, request, integration) scope.add_event_processor(evt_processor) From eac2e8bf1116af326fc06dcd996b8474e6622ba7 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 11:55:05 +0200 Subject: [PATCH 14/45] Cleanup ASGI --- sentry_sdk/integrations/asgi.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sentry_sdk/integrations/asgi.py b/sentry_sdk/integrations/asgi.py index 2c03b7e61a..626d6f18d3 100644 --- a/sentry_sdk/integrations/asgi.py +++ b/sentry_sdk/integrations/asgi.py @@ -39,7 +39,6 @@ _asgi_middleware_applied = ContextVar("sentry_asgi_middleware_applied") _DEFAULT_TRANSACTION_NAME = "generic ASGI request" -_DEFAULT_TRANSACTION_SOURCE = TRANSACTION_SOURCE_UNKNOWN TRANSACTION_STYLE_VALUES = ("endpoint", "url") From 5232d8a87942885e8dcad182bd62f40fe038e8ac Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 11:56:38 +0200 Subject: [PATCH 15/45] Set transaction source in Django --- sentry_sdk/integrations/django/__init__.py | 57 ++++++++++++++-------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/sentry_sdk/integrations/django/__init__.py b/sentry_sdk/integrations/django/__init__.py index d2ca12be4a..ad8156c213 100644 --- a/sentry_sdk/integrations/django/__init__.py +++ b/sentry_sdk/integrations/django/__init__.py @@ -13,6 +13,8 @@ from sentry_sdk.utils import ( HAS_REAL_CONTEXTVARS, CONTEXTVARS_ERROR_MESSAGE, + TRANSACTION_SOURCE_COMPONENT, + TRANSACTION_SOURCE_ROUTE, logger, capture_internal_exceptions, event_from_exception, @@ -319,6 +321,32 @@ def _patch_django_asgi_handler(): patch_django_asgi_handler_impl(ASGIHandler) +def _set_transaction_name_and_source(scope, transaction_style, request): + # type: (Scope, str, WSGIRequest) -> Scope + try: + transaction_name = None + if transaction_style == "function_name": + fn = resolve(request.path).func + transaction_name = transaction_from_function(getattr(fn, "view_class", fn)) + + elif transaction_style == "url": + transaction_name = LEGACY_RESOLVER.resolve(request.path_info) + + source_for_style = { + "function_name": TRANSACTION_SOURCE_COMPONENT, + "url": TRANSACTION_SOURCE_ROUTE, + } + + scope.set_transaction_name( + transaction_name, + source=source_for_style.get(transaction_style), + ) + except Exception: + pass + + return scope + + def _before_get_response(request): # type: (WSGIRequest) -> None hub = Hub.current @@ -330,24 +358,17 @@ def _before_get_response(request): with hub.configure_scope() as scope: # Rely on WSGI middleware to start a trace - try: - if integration.transaction_style == "function_name": - fn = resolve(request.path).func - scope.transaction = transaction_from_function( - getattr(fn, "view_class", fn) - ) - elif integration.transaction_style == "url": - scope.transaction = LEGACY_RESOLVER.resolve(request.path_info) - except Exception: - pass + scope = _set_transaction_name_and_source( + scope, integration.transaction_style, request + ) scope.add_event_processor( _make_event_processor(weakref.ref(request), integration) ) -def _attempt_resolve_again(request, scope): - # type: (WSGIRequest, Scope) -> None +def _attempt_resolve_again(request, scope, transaction_style): + # type: (WSGIRequest, Scope, str) -> None """ Some django middlewares overwrite request.urlconf so we need to respect that contract, @@ -356,13 +377,7 @@ def _attempt_resolve_again(request, scope): if not hasattr(request, "urlconf"): return - try: - scope.transaction = LEGACY_RESOLVER.resolve( - request.path_info, - urlconf=request.urlconf, - ) - except Exception: - pass + _set_transaction_name_and_source(scope, transaction_style, request) def _after_get_response(request): @@ -373,7 +388,7 @@ def _after_get_response(request): return with hub.configure_scope() as scope: - _attempt_resolve_again(request, scope) + _attempt_resolve_again(request, scope, integration.transaction_style) def _patch_get_response(): @@ -438,7 +453,7 @@ def _got_request_exception(request=None, **kwargs): if request is not None and integration.transaction_style == "url": with hub.configure_scope() as scope: - _attempt_resolve_again(request, scope) + _attempt_resolve_again(request, scope, integration.transaction_style) # If an integration is there, a client has to be there. client = hub.client # type: Any From a8718b7a935edef0efef36e154f537f2e98afba9 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 12:14:14 +0200 Subject: [PATCH 16/45] Added tests for transaction name and source in Django --- sentry_sdk/integrations/django/__init__.py | 7 ++++++- tests/integrations/django/test_basic.py | 14 ++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/sentry_sdk/integrations/django/__init__.py b/sentry_sdk/integrations/django/__init__.py index ad8156c213..14f1951cd0 100644 --- a/sentry_sdk/integrations/django/__init__.py +++ b/sentry_sdk/integrations/django/__init__.py @@ -330,7 +330,12 @@ def _set_transaction_name_and_source(scope, transaction_style, request): transaction_name = transaction_from_function(getattr(fn, "view_class", fn)) elif transaction_style == "url": - transaction_name = LEGACY_RESOLVER.resolve(request.path_info) + if hasattr(request, "urlconf"): + transaction_name = LEGACY_RESOLVER.resolve( + request.path_info, urlconf=request.urlconf + ) + else: + transaction_name = LEGACY_RESOLVER.resolve(request.path_info) source_for_style = { "function_name": TRANSACTION_SOURCE_COMPONENT, diff --git a/tests/integrations/django/test_basic.py b/tests/integrations/django/test_basic.py index 6106131375..6195811fe0 100644 --- a/tests/integrations/django/test_basic.py +++ b/tests/integrations/django/test_basic.py @@ -469,14 +469,19 @@ def test_django_connect_breadcrumbs( @pytest.mark.parametrize( - "transaction_style,expected_transaction", + "transaction_style,expected_transaction,expected_source", [ - ("function_name", "tests.integrations.django.myapp.views.message"), - ("url", "/message"), + ("function_name", "tests.integrations.django.myapp.views.message", "component"), + ("url", "/message", "route"), ], ) def test_transaction_style( - sentry_init, client, capture_events, transaction_style, expected_transaction + sentry_init, + client, + capture_events, + transaction_style, + expected_transaction, + expected_source, ): sentry_init( integrations=[DjangoIntegration(transaction_style=transaction_style)], @@ -488,6 +493,7 @@ def test_transaction_style( (event,) = events assert event["transaction"] == expected_transaction + assert event["transaction_info"] == {"source": expected_source} def test_request_body(sentry_init, client, capture_events): From fbe1bd2200428e0b87ee02e31a2b253814826692 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 12:27:20 +0200 Subject: [PATCH 17/45] Added source to Transaction class --- sentry_sdk/tracing.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index fe53386597..ad1cd20f68 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -6,7 +6,7 @@ import sentry_sdk -from sentry_sdk.utils import logger +from sentry_sdk.utils import TRANSACTION_SOURCE_UNKNOWN, logger from sentry_sdk._types import MYPY @@ -498,6 +498,7 @@ def get_trace_context(self): class Transaction(Span): __slots__ = ( "name", + "source", "parent_sampled", # the sentry portion of the `tracestate` header used to transmit # correlation context for server-side dynamic sampling, of the form @@ -513,6 +514,7 @@ class Transaction(Span): def __init__( self, name="", # type: str + source=TRANSACTION_SOURCE_UNKNOWN, # type: str parent_sampled=None, # type: Optional[bool] sentry_tracestate=None, # type: Optional[str] third_party_tracestate=None, # type: Optional[str] @@ -531,6 +533,7 @@ def __init__( name = kwargs.pop("transaction") Span.__init__(self, **kwargs) self.name = name + self.source = source self.parent_sampled = parent_sampled # if tracestate isn't inherited and set here, it will get set lazily, # either the first time an outgoing request needs it for a header or the @@ -543,7 +546,7 @@ def __init__( def __repr__(self): # type: () -> str return ( - "<%s(name=%r, op=%r, trace_id=%r, span_id=%r, parent_span_id=%r, sampled=%r)>" + "<%s(name=%r, op=%r, trace_id=%r, span_id=%r, parent_span_id=%r, sampled=%r, source=%r)>" % ( self.__class__.__name__, self.name, @@ -552,6 +555,7 @@ def __repr__(self): self.span_id, self.parent_span_id, self.sampled, + self.source, ) ) @@ -621,6 +625,7 @@ def finish(self, hub=None): event = { "type": "transaction", "transaction": self.name, + "transaction_info": {"source": self.source}, "contexts": {"trace": self.get_trace_context()}, "tags": self._tags, "timestamp": self.timestamp, @@ -648,6 +653,7 @@ def to_json(self): rv = super(Transaction, self).to_json() rv["name"] = self.name + rv["source"] = self.source rv["sampled"] = self.sampled return rv From 66c5cffff895e489f0e4b98d53b3fc97634c5e1e Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 12:27:40 +0200 Subject: [PATCH 18/45] Added transaction source to Celery --- sentry_sdk/integrations/celery.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/integrations/celery.py b/sentry_sdk/integrations/celery.py index 743e2cfb50..f3aed57be3 100644 --- a/sentry_sdk/integrations/celery.py +++ b/sentry_sdk/integrations/celery.py @@ -3,7 +3,11 @@ import sys from sentry_sdk.hub import Hub -from sentry_sdk.utils import capture_internal_exceptions, event_from_exception +from sentry_sdk.utils import ( + TRANSACTION_SOURCE_TASK, + capture_internal_exceptions, + event_from_exception, +) from sentry_sdk.tracing import Transaction from sentry_sdk._compat import reraise from sentry_sdk.integrations import Integration, DidNotEnable @@ -157,6 +161,7 @@ def _inner(*args, **kwargs): ) transaction.name = task.name + transaction.source = TRANSACTION_SOURCE_TASK transaction.set_status("ok") if transaction is None: From 1db75acb25135c25e552a46462c51fd1681dd96f Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 12:41:55 +0200 Subject: [PATCH 19/45] Quick refactoring --- sentry_sdk/integrations/falcon.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/integrations/falcon.py b/sentry_sdk/integrations/falcon.py index 8129fab46b..1c09cae28b 100644 --- a/sentry_sdk/integrations/falcon.py +++ b/sentry_sdk/integrations/falcon.py @@ -200,7 +200,7 @@ def _exception_leads_to_http_5xx(ex): def _make_request_event_processor(req, integration): # type: (falcon.Request, FalconIntegration) -> EventProcessor - def inner(event, hint): + def event_processor(event, hint): # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any] if integration.transaction_style == "uri_template": event["transaction"] = req.uri_template @@ -212,4 +212,4 @@ def inner(event, hint): return event - return inner + return event_processor From fbdf13269bf6eb0eb867b10ed65accc5bf80061c Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 12:45:59 +0200 Subject: [PATCH 20/45] Added transaction source to Falcon. --- sentry_sdk/integrations/falcon.py | 32 ++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/sentry_sdk/integrations/falcon.py b/sentry_sdk/integrations/falcon.py index 1c09cae28b..60eeccc709 100644 --- a/sentry_sdk/integrations/falcon.py +++ b/sentry_sdk/integrations/falcon.py @@ -4,7 +4,12 @@ from sentry_sdk.integrations import Integration, DidNotEnable from sentry_sdk.integrations._wsgi_common import RequestExtractor from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware -from sentry_sdk.utils import capture_internal_exceptions, event_from_exception +from sentry_sdk.utils import ( + TRANSACTION_SOURCE_COMPONENT, + TRANSACTION_SOURCE_ROUTE, + capture_internal_exceptions, + event_from_exception, +) from sentry_sdk._types import MYPY @@ -197,15 +202,32 @@ def _exception_leads_to_http_5xx(ex): return is_server_error or is_unhandled_error +def _set_transaction_name_and_source(event, transaction_style, request): + # type: (Dict[str, Any], str, Any) -> Dict[str, Any] + + name_for_style = { + "uri_template": request.uri_template, + "path": request.path, + } + source_for_style = { + "uri_template": TRANSACTION_SOURCE_ROUTE, + "path": TRANSACTION_SOURCE_COMPONENT, + } + + event["transaction"] = name_for_style[transaction_style] + event["transaction_info"] = {"source": source_for_style[transaction_style]} + + return event + + def _make_request_event_processor(req, integration): # type: (falcon.Request, FalconIntegration) -> EventProcessor def event_processor(event, hint): # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any] - if integration.transaction_style == "uri_template": - event["transaction"] = req.uri_template - elif integration.transaction_style == "path": - event["transaction"] = req.path + event = _set_transaction_name_and_source( + event, integration.transaction_style, req + ) with capture_internal_exceptions(): FalconRequestExtractor(req).extract_into_event(event) From e1e209791ca38a8e4c89e75b2f870e369ac06679 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 13:07:07 +0200 Subject: [PATCH 21/45] Added tests for transaction source to Falcon --- sentry_sdk/integrations/falcon.py | 7 +++---- tests/integrations/falcon/test_falcon.py | 26 ++++++++++++++++++++---- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/sentry_sdk/integrations/falcon.py b/sentry_sdk/integrations/falcon.py index 60eeccc709..4bd7208094 100644 --- a/sentry_sdk/integrations/falcon.py +++ b/sentry_sdk/integrations/falcon.py @@ -5,8 +5,8 @@ from sentry_sdk.integrations._wsgi_common import RequestExtractor from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware from sentry_sdk.utils import ( - TRANSACTION_SOURCE_COMPONENT, TRANSACTION_SOURCE_ROUTE, + TRANSACTION_SOURCE_URL, capture_internal_exceptions, event_from_exception, ) @@ -203,15 +203,14 @@ def _exception_leads_to_http_5xx(ex): def _set_transaction_name_and_source(event, transaction_style, request): - # type: (Dict[str, Any], str, Any) -> Dict[str, Any] - + # type: (Dict[str, Any], str, falcon.Request) -> Dict[str, Any] name_for_style = { "uri_template": request.uri_template, "path": request.path, } source_for_style = { "uri_template": TRANSACTION_SOURCE_ROUTE, - "path": TRANSACTION_SOURCE_COMPONENT, + "path": TRANSACTION_SOURCE_URL, } event["transaction"] = name_for_style[transaction_style] diff --git a/tests/integrations/falcon/test_falcon.py b/tests/integrations/falcon/test_falcon.py index 84e8d228f0..96aa0ee036 100644 --- a/tests/integrations/falcon/test_falcon.py +++ b/tests/integrations/falcon/test_falcon.py @@ -21,8 +21,14 @@ def on_get(self, req, resp): sentry_sdk.capture_message("hi") resp.media = "hi" + class MessageByIdResource: + def on_get(self, req, resp, message_id): + sentry_sdk.capture_message("hi") + resp.media = "hi" + app = falcon.API() app.add_route("/message", MessageResource()) + app.add_route("/message/{message_id:int}", MessageByIdResource()) return app @@ -53,22 +59,34 @@ def test_has_context(sentry_init, capture_events, make_client): @pytest.mark.parametrize( - "transaction_style,expected_transaction", - [("uri_template", "/message"), ("path", "/message")], + "url,transaction_style,expected_transaction,expected_source", + [ + ("/message", "uri_template", "/message", "route"), + ("/message", "path", "/message", "url"), + ("/message/123456", "uri_template", "/message/{message_id:int}", "route"), + ("/message/123456", "path", "/message/123456", "url"), + ], ) def test_transaction_style( - sentry_init, make_client, capture_events, transaction_style, expected_transaction + sentry_init, + make_client, + capture_events, + url, + transaction_style, + expected_transaction, + expected_source, ): integration = FalconIntegration(transaction_style=transaction_style) sentry_init(integrations=[integration]) events = capture_events() client = make_client() - response = client.simulate_get("/message") + response = client.simulate_get(url) assert response.status == falcon.HTTP_200 (event,) = events assert event["transaction"] == expected_transaction + assert event["transaction_info"] == {"source": expected_source} def test_unhandled_errors(sentry_init, capture_exceptions, capture_events): From 9f4b9b648d6b6437c797de81fb1dd5dc8f0cd435 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 13:15:10 +0200 Subject: [PATCH 22/45] Added transaction source to Pyramid --- sentry_sdk/integrations/pyramid.py | 41 +++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/sentry_sdk/integrations/pyramid.py b/sentry_sdk/integrations/pyramid.py index 07142254d2..4ddbe69967 100644 --- a/sentry_sdk/integrations/pyramid.py +++ b/sentry_sdk/integrations/pyramid.py @@ -5,7 +5,13 @@ import weakref from sentry_sdk.hub import Hub, _should_send_default_pii -from sentry_sdk.utils import capture_internal_exceptions, event_from_exception +from sentry_sdk.scope import Scope +from sentry_sdk.utils import ( + TRANSACTION_SOURCE_COMPONENT, + TRANSACTION_SOURCE_ROUTE, + capture_internal_exceptions, + event_from_exception, +) from sentry_sdk._compat import reraise, iteritems from sentry_sdk.integrations import Integration, DidNotEnable @@ -76,14 +82,9 @@ def sentry_patched_call_view(registry, request, *args, **kwargs): if integration is not None: with hub.configure_scope() as scope: - try: - if integration.transaction_style == "route_name": - scope.transaction = request.matched_route.name - elif integration.transaction_style == "route_pattern": - scope.transaction = request.matched_route.pattern - except Exception: - pass - + scope = _set_transaction_name_and_source( + scope, integration.transaction_style, request + ) scope.add_event_processor( _make_event_processor(weakref.ref(request), integration) ) @@ -156,6 +157,28 @@ def _capture_exception(exc_info): hub.capture_event(event, hint=hint) +def _set_transaction_name_and_source(scope, transaction_style, request): + # type: (Scope, str, Request) -> Scope + try: + name_for_style = { + "route_name": request.matched_route.name, + "route_pattern": request.matched_route.pattern, + } + source_for_style = { + "route_name": TRANSACTION_SOURCE_COMPONENT, + "route_pattern": TRANSACTION_SOURCE_ROUTE, + } + + scope.set_transaction_name( + name_for_style.get(transaction_style), + source=source_for_style.get(transaction_style), + ) + except Exception: + pass + + return scope + + class PyramidRequestExtractor(RequestExtractor): def url(self): # type: () -> str From 1125f3e4551387f98e923665c58bd3ad95a69917 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 13:23:17 +0200 Subject: [PATCH 23/45] Added tests for transaction source to Pyramid. --- tests/integrations/pyramid/test_pyramid.py | 33 +++++++++++++++++----- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/tests/integrations/pyramid/test_pyramid.py b/tests/integrations/pyramid/test_pyramid.py index 9c6fd51222..c49f8b4475 100644 --- a/tests/integrations/pyramid/test_pyramid.py +++ b/tests/integrations/pyramid/test_pyramid.py @@ -26,12 +26,19 @@ def hi(request): return Response("hi") +def hi_with_id(request): + capture_message("hi with id") + return Response("hi with id") + + @pytest.fixture def pyramid_config(): config = pyramid.testing.setUp() try: config.add_route("hi", "/message") config.add_view(hi, route_name="hi") + config.add_route("hi_with_id", "/message/{message_id}") + config.add_view(hi_with_id, route_name="hi_with_id") yield config finally: pyramid.testing.tearDown() @@ -89,13 +96,13 @@ def test_has_context(route, get_client, sentry_init, capture_events): sentry_init(integrations=[PyramidIntegration()]) events = capture_events() - @route("/message/{msg}") + @route("/context_message/{msg}") def hi2(request): capture_message(request.matchdict["msg"]) return Response("hi") client = get_client() - client.get("/message/yoo") + client.get("/context_message/yoo") (event,) = events assert event["message"] == "yoo" @@ -104,26 +111,38 @@ def hi2(request): "headers": {"Host": "localhost"}, "method": "GET", "query_string": "", - "url": "http://localhost/message/yoo", + "url": "http://localhost/context_message/yoo", } assert event["transaction"] == "hi2" @pytest.mark.parametrize( - "transaction_style,expected_transaction", - [("route_name", "hi"), ("route_pattern", "/message")], + "url,transaction_style,expected_transaction,expected_source", + [ + ("/message", "route_name", "hi", "component"), + ("/message", "route_pattern", "/message", "route"), + ("/message/123456", "route_name", "hi_with_id", "component"), + ("/message/123456", "route_pattern", "/message/{message_id}", "route"), + ], ) def test_transaction_style( - sentry_init, get_client, capture_events, transaction_style, expected_transaction + sentry_init, + get_client, + capture_events, + url, + transaction_style, + expected_transaction, + expected_source, ): sentry_init(integrations=[PyramidIntegration(transaction_style=transaction_style)]) events = capture_events() client = get_client() - client.get("/message") + client.get(url) (event,) = events assert event["transaction"] == expected_transaction + assert event["transaction_info"] == {"source": expected_source} def test_large_json_request(sentry_init, capture_events, route, get_client): From ff26d8e16cbec736dc5f7dac64bb6eb11c598670 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 13:31:30 +0200 Subject: [PATCH 24/45] Added transaction source to quart. --- sentry_sdk/integrations/quart.py | 41 +++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/sentry_sdk/integrations/quart.py b/sentry_sdk/integrations/quart.py index 411817c708..ee91057a44 100644 --- a/sentry_sdk/integrations/quart.py +++ b/sentry_sdk/integrations/quart.py @@ -4,7 +4,13 @@ from sentry_sdk.integrations import DidNotEnable, Integration from sentry_sdk.integrations._wsgi_common import _filter_headers from sentry_sdk.integrations.asgi import SentryAsgiMiddleware -from sentry_sdk.utils import capture_internal_exceptions, event_from_exception +from sentry_sdk.scope import Scope +from sentry_sdk.utils import ( + TRANSACTION_SOURCE_COMPONENT, + TRANSACTION_SOURCE_ROUTE, + capture_internal_exceptions, + event_from_exception, +) from sentry_sdk._types import MYPY @@ -79,6 +85,29 @@ async def sentry_patched_asgi_app(self, scope, receive, send): Quart.__call__ = sentry_patched_asgi_app +def _set_transaction_name_and_source(scope, transaction_style, request): + # type: (Scope, str, Request) -> Scope + + try: + name_for_style = { + "url": request.url_rule.rule, + "endpoint": request.url_rule.endpoint, + } + source_for_style = { + "url": TRANSACTION_SOURCE_ROUTE, + "endpoint": TRANSACTION_SOURCE_COMPONENT, + } + + scope.set_transaction_name( + name_for_style.get(transaction_style), + source=source_for_style.get(transaction_style), + ) + except Exception: + pass + + return scope + + def _request_websocket_started(sender, **kwargs): # type: (Quart, **Any) -> None hub = Hub.current @@ -95,13 +124,9 @@ def _request_websocket_started(sender, **kwargs): # Set the transaction name here, but rely on ASGI middleware # to actually start the transaction - try: - if integration.transaction_style == "endpoint": - scope.transaction = request_websocket.url_rule.endpoint - elif integration.transaction_style == "url": - scope.transaction = request_websocket.url_rule.rule - except Exception: - pass + scope = _set_transaction_name_and_source( + scope, integration.transaction_style, request_websocket + ) evt_processor = _make_request_event_processor( app, request_websocket, integration From a8251ccc356ac82930dbb0cfec93bf56861b989f Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 13:41:59 +0200 Subject: [PATCH 25/45] Added tests for transaction source to Quart --- sentry_sdk/integrations/asgi.py | 2 +- tests/integrations/quart/test_quart.py | 23 ++++++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/sentry_sdk/integrations/asgi.py b/sentry_sdk/integrations/asgi.py index 626d6f18d3..e3dce9b627 100644 --- a/sentry_sdk/integrations/asgi.py +++ b/sentry_sdk/integrations/asgi.py @@ -208,7 +208,7 @@ def _set_transaction_name_and_source(self, event, transaction_style, asgi_scope) != _DEFAULT_TRANSACTION_NAME ) if transaction_name_already_set: - return + return event if transaction_style == "endpoint": endpoint = asgi_scope.get("endpoint") diff --git a/tests/integrations/quart/test_quart.py b/tests/integrations/quart/test_quart.py index d827b3c4aa..0665209570 100644 --- a/tests/integrations/quart/test_quart.py +++ b/tests/integrations/quart/test_quart.py @@ -35,6 +35,11 @@ async def hi(): capture_message("hi") return "ok" + @app.route("/message/") + async def hi_with_id(message_id): + capture_message("hi with id") + return "ok with id" + return app @@ -63,10 +68,22 @@ async def test_has_context(sentry_init, app, capture_events): @pytest.mark.asyncio @pytest.mark.parametrize( - "transaction_style,expected_transaction", [("endpoint", "hi"), ("url", "/message")] + "url,transaction_style,expected_transaction,expected_source", + [ + ("/message", "endpoint", "hi", "component"), + ("/message", "url", "/message", "route"), + ("/message/123456", "endpoint", "hi_with_id", "component"), + ("/message/123456", "url", "/message/", "route"), + ], ) async def test_transaction_style( - sentry_init, app, capture_events, transaction_style, expected_transaction + sentry_init, + app, + capture_events, + url, + transaction_style, + expected_transaction, + expected_source, ): sentry_init( integrations=[ @@ -76,7 +93,7 @@ async def test_transaction_style( events = capture_events() client = app.test_client() - response = await client.get("/message") + response = await client.get(url) assert response.status_code == 200 (event,) = events From 177fc76f159dea972fb49ff3449a489966e318c6 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 14:14:52 +0200 Subject: [PATCH 26/45] Added transaction source and tests for Sanic --- sentry_sdk/integrations/sanic.py | 14 +++++++++++--- tests/integrations/sanic/test_sanic.py | 26 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/integrations/sanic.py b/sentry_sdk/integrations/sanic.py index 4e20cc9ece..f54bedd630 100644 --- a/sentry_sdk/integrations/sanic.py +++ b/sentry_sdk/integrations/sanic.py @@ -5,6 +5,7 @@ from sentry_sdk._compat import urlparse, reraise from sentry_sdk.hub import Hub from sentry_sdk.utils import ( + TRANSACTION_SOURCE_COMPONENT, capture_internal_exceptions, event_from_exception, HAS_REAL_CONTEXTVARS, @@ -191,7 +192,9 @@ async def _set_transaction(request, route, **kwargs): with capture_internal_exceptions(): with hub.configure_scope() as scope: route_name = route.name.replace(request.app.name, "").strip(".") - scope.transaction = route_name + scope.set_transaction_name( + route_name, source=TRANSACTION_SOURCE_COMPONENT + ) def _sentry_error_handler_lookup(self, exception, *args, **kwargs): @@ -268,9 +271,14 @@ def _legacy_router_get(self, *args): # Format: app_name.route_name sanic_route = sanic_route[len(sanic_app_name) + 1 :] - scope.transaction = sanic_route + scope.set_transaction_name( + sanic_route, source=TRANSACTION_SOURCE_COMPONENT + ) else: - scope.transaction = rv[0].__name__ + scope.set_transaction_name( + rv[0].__name__, source=TRANSACTION_SOURCE_COMPONENT + ) + return rv diff --git a/tests/integrations/sanic/test_sanic.py b/tests/integrations/sanic/test_sanic.py index b91f94bfe9..f8fdd696bc 100644 --- a/tests/integrations/sanic/test_sanic.py +++ b/tests/integrations/sanic/test_sanic.py @@ -30,6 +30,11 @@ def hi(request): capture_message("hi") return response.text("ok") + @app.route("/message/") + def hi_with_id(request, message_id): + capture_message("hi with id") + return response.text("ok with id") + return app @@ -62,6 +67,27 @@ def test_request_data(sentry_init, app, capture_events): assert "transaction" not in event +@pytest.mark.parametrize( + "url,expected_transaction,expected_source", + [ + ("/message", "hi", "component"), + ("/message/123456", "hi_with_id", "component"), + ], +) +def test_transaction( + sentry_init, app, capture_events, url, expected_transaction, expected_source +): + sentry_init(integrations=[SanicIntegration()]) + events = capture_events() + + request, response = app.test_client.get(url) + assert response.status == 200 + + (event,) = events + assert event["transaction"] == expected_transaction + assert event["transaction_info"] == {"source": expected_source} + + def test_errors(sentry_init, app, capture_events): sentry_init(integrations=[SanicIntegration()]) events = capture_events() From 4a95775a2dbcfac242ce01772f47410fe45f4941 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 14:35:31 +0200 Subject: [PATCH 27/45] Added transaction source and tests for Tornado., --- sentry_sdk/integrations/tornado.py | 2 ++ tests/integrations/tornado/test_tornado.py | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/sentry_sdk/integrations/tornado.py b/sentry_sdk/integrations/tornado.py index 443ebefaa8..60a3bda643 100644 --- a/sentry_sdk/integrations/tornado.py +++ b/sentry_sdk/integrations/tornado.py @@ -7,6 +7,7 @@ from sentry_sdk.utils import ( HAS_REAL_CONTEXTVARS, CONTEXTVARS_ERROR_MESSAGE, + TRANSACTION_SOURCE_COMPONENT, event_from_exception, capture_internal_exceptions, transaction_from_function, @@ -157,6 +158,7 @@ def tornado_processor(event, hint): with capture_internal_exceptions(): method = getattr(handler, handler.request.method.lower()) event["transaction"] = transaction_from_function(method) + event["transaction_info"] = {"source": TRANSACTION_SOURCE_COMPONENT} with capture_internal_exceptions(): extractor = TornadoRequestExtractor(request) diff --git a/tests/integrations/tornado/test_tornado.py b/tests/integrations/tornado/test_tornado.py index 1c5137f2b2..f59781dc21 100644 --- a/tests/integrations/tornado/test_tornado.py +++ b/tests/integrations/tornado/test_tornado.py @@ -96,6 +96,7 @@ def test_basic(tornado_testcase, sentry_init, capture_events): event["transaction"] == "tests.integrations.tornado.test_tornado.CrashingHandler.get" ) + assert event["transaction_info"] == {"source": "component"} with configure_scope() as scope: assert not scope._tags @@ -129,6 +130,9 @@ def test_transactions(tornado_testcase, sentry_init, capture_events, handler, co assert client_tx["type"] == "transaction" assert client_tx["transaction"] == "client" + assert client_tx["transaction_info"] == { + "source": "unknown" + } # because this is just the start_transaction() above. if server_error is not None: assert server_error["exception"]["values"][0]["type"] == "ZeroDivisionError" @@ -136,6 +140,7 @@ def test_transactions(tornado_testcase, sentry_init, capture_events, handler, co server_error["transaction"] == "tests.integrations.tornado.test_tornado.CrashingHandler.post" ) + assert server_error["transaction_info"] == {"source": "component"} if code == 200: assert ( @@ -148,6 +153,7 @@ def test_transactions(tornado_testcase, sentry_init, capture_events, handler, co == "tests.integrations.tornado.test_tornado.CrashingHandler.post" ) + assert server_tx["transaction_info"] == {"source": "component"} assert server_tx["type"] == "transaction" request = server_tx["request"] From 57f9c970401e91fb3a1373547c9f5aa67946914f Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 15:22:13 +0200 Subject: [PATCH 28/45] Added transaction source and tests for AIOHTTP --- sentry_sdk/integrations/aiohttp.py | 12 +++++++++++- sentry_sdk/scope.py | 2 ++ tests/integrations/aiohttp/test_aiohttp.py | 22 ++++++++++++++++++---- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/sentry_sdk/integrations/aiohttp.py b/sentry_sdk/integrations/aiohttp.py index 8a828b2fe3..3dd5fa4c92 100644 --- a/sentry_sdk/integrations/aiohttp.py +++ b/sentry_sdk/integrations/aiohttp.py @@ -11,6 +11,8 @@ ) from sentry_sdk.tracing import Transaction from sentry_sdk.utils import ( + TRANSACTION_SOURCE_COMPONENT, + TRANSACTION_SOURCE_ROUTE, capture_internal_exceptions, event_from_exception, transaction_from_function, @@ -148,7 +150,15 @@ async def sentry_urldispatcher_resolve(self, request): if name is not None: with Hub.current.configure_scope() as scope: - scope.transaction = name + source_for_style = { + "handler_name": TRANSACTION_SOURCE_COMPONENT, + "method_and_path_pattern": TRANSACTION_SOURCE_ROUTE, + } + + scope.set_transaction_name( + name, + source=source_for_style.get(integration.transaction_style), + ) return rv diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 6f3909ecbd..e0a2dc7a8d 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -192,6 +192,8 @@ def set_transaction_name(self, name, source=None): if self._span and self._span.containing_transaction: self._span.containing_transaction.name = name + if source: + self._span.containing_transaction.source = source if source: self._transaction_info["source"] = source diff --git a/tests/integrations/aiohttp/test_aiohttp.py b/tests/integrations/aiohttp/test_aiohttp.py index 5c590bcdfa..3375ee76ad 100644 --- a/tests/integrations/aiohttp/test_aiohttp.py +++ b/tests/integrations/aiohttp/test_aiohttp.py @@ -196,17 +196,30 @@ async def hello(request): @pytest.mark.parametrize( - "transaction_style,expected_transaction", + "url,transaction_style,expected_transaction,expected_source", [ ( + "/message", "handler_name", "tests.integrations.aiohttp.test_aiohttp.test_transaction_style..hello", + "component", + ), + ( + "/message", + "method_and_path_pattern", + "GET /{var}", + "route", ), - ("method_and_path_pattern", "GET /{var}"), ], ) async def test_transaction_style( - sentry_init, aiohttp_client, capture_events, transaction_style, expected_transaction + sentry_init, + aiohttp_client, + capture_events, + url, + transaction_style, + expected_transaction, + expected_source, ): sentry_init( integrations=[AioHttpIntegration(transaction_style=transaction_style)], @@ -222,13 +235,14 @@ async def hello(request): events = capture_events() client = await aiohttp_client(app) - resp = await client.get("/1") + resp = await client.get(url) assert resp.status == 200 (event,) = events assert event["type"] == "transaction" assert event["transaction"] == expected_transaction + assert event["transaction_info"] == {"source": expected_source} async def test_traces_sampler_gets_request_object_in_sampling_context( From b2165cc5cded09c97cb84008d61a1df174f73014 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 15:51:25 +0200 Subject: [PATCH 29/45] Added transaction source and tests for Chalice --- sentry_sdk/integrations/chalice.py | 7 ++++- tests/integrations/chalice/test_chalice.py | 36 ++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/integrations/chalice.py b/sentry_sdk/integrations/chalice.py index 109862bd90..46b3933829 100644 --- a/sentry_sdk/integrations/chalice.py +++ b/sentry_sdk/integrations/chalice.py @@ -5,6 +5,7 @@ from sentry_sdk.integrations import Integration, DidNotEnable from sentry_sdk.integrations.aws_lambda import _make_request_event_processor from sentry_sdk.utils import ( + TRANSACTION_SOURCE_COMPONENT, capture_internal_exceptions, event_from_exception, ) @@ -65,7 +66,11 @@ def wrapped_view_function(**function_args): with hub.push_scope() as scope: with capture_internal_exceptions(): configured_time = app.lambda_context.get_remaining_time_in_millis() - scope.transaction = app.lambda_context.function_name + scope.set_transaction_name( + app.lambda_context.function_name, + source=TRANSACTION_SOURCE_COMPONENT, + ) + scope.add_event_processor( _make_request_event_processor( app.current_request.to_dict(), diff --git a/tests/integrations/chalice/test_chalice.py b/tests/integrations/chalice/test_chalice.py index 8bb33a5cb6..4162a55623 100644 --- a/tests/integrations/chalice/test_chalice.py +++ b/tests/integrations/chalice/test_chalice.py @@ -4,6 +4,7 @@ from chalice.local import LambdaContext, LocalGateway from sentry_sdk.integrations.chalice import ChaliceIntegration +from sentry_sdk import capture_message from pytest_chalice.handlers import RequestHandler @@ -41,6 +42,16 @@ def has_request(): def badrequest(): raise BadRequestError("bad-request") + @app.route("/message") + def hi(): + capture_message("hi") + return {"status": "ok"} + + @app.route("/message/{message_id}") + def hi_with_id(message_id): + capture_message("hi again") + return {"status": "ok"} + LocalGateway._generate_lambda_context = _generate_lambda_context return app @@ -109,3 +120,28 @@ def test_bad_reques(client: RequestHandler) -> None: ("Message", "BadRequestError: bad-request"), ] ) + + +@pytest.mark.parametrize( + "url,expected_transaction,expected_source", + [ + ("/message", "api_handler", "component"), + ("/message/123456", "api_handler", "component"), + ], +) +def test_transaction( + app, + client: RequestHandler, + capture_events, + url, + expected_transaction, + expected_source, +): + events = capture_events() + + response = client.get(url) + assert response.status_code == 200 + + (event,) = events + assert event["transaction"] == expected_transaction + assert event["transaction_info"] == {"source": expected_source} From 1e0009be0011a4f88b0239e23f2bd208aa6c7ea4 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 16:10:49 +0200 Subject: [PATCH 30/45] Added transaction source and tests for GCP --- sentry_sdk/integrations/gcp.py | 6 +++++- tests/integrations/gcp/test_gcp.py | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/integrations/gcp.py b/sentry_sdk/integrations/gcp.py index 118970e9d8..9cc6d42597 100644 --- a/sentry_sdk/integrations/gcp.py +++ b/sentry_sdk/integrations/gcp.py @@ -6,6 +6,7 @@ from sentry_sdk.tracing import Transaction from sentry_sdk._compat import reraise from sentry_sdk.utils import ( + TRANSACTION_SOURCE_COMPONENT, AnnotatedValue, capture_internal_exceptions, event_from_exception, @@ -81,7 +82,10 @@ def sentry_func(functionhandler, gcp_event, *args, **kwargs): if hasattr(gcp_event, "headers"): headers = gcp_event.headers transaction = Transaction.continue_from_headers( - headers, op="serverless.function", name=environ.get("FUNCTION_NAME", "") + headers, + op="serverless.function", + name=environ.get("FUNCTION_NAME", ""), + source=TRANSACTION_SOURCE_COMPONENT, ) sampling_context = { "gcp_env": { diff --git a/tests/integrations/gcp/test_gcp.py b/tests/integrations/gcp/test_gcp.py index 78ac8f2746..5f41300bcb 100644 --- a/tests/integrations/gcp/test_gcp.py +++ b/tests/integrations/gcp/test_gcp.py @@ -255,6 +255,7 @@ def cloud_function(functionhandler, event): assert envelope["type"] == "transaction" assert envelope["contexts"]["trace"]["op"] == "serverless.function" assert envelope["transaction"].startswith("Google Cloud function") + assert envelope["transaction_info"] == {"source": "component"} assert envelope["transaction"] in envelope["request"]["url"] From ddf0fe666e39eeaea02957d046021a0acf108114 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 16:20:26 +0200 Subject: [PATCH 31/45] Added transaction source and tests for AWS lambda --- sentry_sdk/integrations/aws_lambda.py | 6 +++++- tests/integrations/aws_lambda/test_aws.py | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/integrations/aws_lambda.py b/sentry_sdk/integrations/aws_lambda.py index 10b5025abe..ba28954e11 100644 --- a/sentry_sdk/integrations/aws_lambda.py +++ b/sentry_sdk/integrations/aws_lambda.py @@ -6,6 +6,7 @@ from sentry_sdk.tracing import Transaction from sentry_sdk._compat import reraise from sentry_sdk.utils import ( + TRANSACTION_SOURCE_COMPONENT, AnnotatedValue, capture_internal_exceptions, event_from_exception, @@ -139,7 +140,10 @@ def sentry_handler(aws_event, aws_context, *args, **kwargs): if headers is None: headers = {} transaction = Transaction.continue_from_headers( - headers, op="serverless.function", name=aws_context.function_name + headers, + op="serverless.function", + name=aws_context.function_name, + source=TRANSACTION_SOURCE_COMPONENT, ) with hub.start_transaction( transaction, diff --git a/tests/integrations/aws_lambda/test_aws.py b/tests/integrations/aws_lambda/test_aws.py index c9084beb14..c6fb54b94f 100644 --- a/tests/integrations/aws_lambda/test_aws.py +++ b/tests/integrations/aws_lambda/test_aws.py @@ -362,6 +362,7 @@ def test_handler(event, context): assert envelope["type"] == "transaction" assert envelope["contexts"]["trace"]["op"] == "serverless.function" assert envelope["transaction"].startswith("test_function_") + assert envelope["transaction_info"] == {"source": "component"} assert envelope["transaction"] in envelope["request"]["url"] @@ -390,6 +391,7 @@ def test_handler(event, context): assert envelope["type"] == "transaction" assert envelope["contexts"]["trace"]["op"] == "serverless.function" assert envelope["transaction"].startswith("test_function_") + assert envelope["transaction_info"] == {"source": "component"} assert envelope["transaction"] in envelope["request"]["url"] From fa6ec707fd24f0e282ab9af19738cfd629a8ab1a Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 16:56:12 +0200 Subject: [PATCH 32/45] Improved typing --- sentry_sdk/integrations/aiohttp.py | 2 +- sentry_sdk/integrations/bottle.py | 2 +- sentry_sdk/integrations/chalice.py | 6 +++--- sentry_sdk/integrations/django/__init__.py | 10 ++++++---- sentry_sdk/integrations/falcon.py | 2 +- sentry_sdk/integrations/flask.py | 10 +++++----- sentry_sdk/integrations/pyramid.py | 6 +++--- sentry_sdk/integrations/quart.py | 6 +++--- 8 files changed, 23 insertions(+), 21 deletions(-) diff --git a/sentry_sdk/integrations/aiohttp.py b/sentry_sdk/integrations/aiohttp.py index 3dd5fa4c92..70edee4a5d 100644 --- a/sentry_sdk/integrations/aiohttp.py +++ b/sentry_sdk/integrations/aiohttp.py @@ -157,7 +157,7 @@ async def sentry_urldispatcher_resolve(self, request): scope.set_transaction_name( name, - source=source_for_style.get(integration.transaction_style), + source=source_for_style[integration.transaction_style], ) return rv diff --git a/sentry_sdk/integrations/bottle.py b/sentry_sdk/integrations/bottle.py index 828b087cca..8af645d788 100644 --- a/sentry_sdk/integrations/bottle.py +++ b/sentry_sdk/integrations/bottle.py @@ -42,7 +42,7 @@ class BottleIntegration(Integration): identifier = "bottle" - transaction_style = None + transaction_style = "" def __init__(self, transaction_style="endpoint"): # type: (str) -> None diff --git a/sentry_sdk/integrations/chalice.py b/sentry_sdk/integrations/chalice.py index 46b3933829..ed6cdc7ce4 100644 --- a/sentry_sdk/integrations/chalice.py +++ b/sentry_sdk/integrations/chalice.py @@ -12,9 +12,9 @@ from sentry_sdk._types import MYPY from sentry_sdk._functools import wraps -import chalice # type: ignore +import chalice from chalice import Chalice, ChaliceViewError -from chalice.app import EventSourceHandler as ChaliceEventSourceHandler # type: ignore +from chalice.app import EventSourceHandler as ChaliceEventSourceHandler if MYPY: from typing import Any @@ -30,7 +30,7 @@ raise DidNotEnable("Chalice is not installed") -class EventSourceHandler(ChaliceEventSourceHandler): # type: ignore +class EventSourceHandler(ChaliceEventSourceHandler): def __call__(self, event, context): # type: (Any, Any) -> Any hub = Hub.current diff --git a/sentry_sdk/integrations/django/__init__.py b/sentry_sdk/integrations/django/__init__.py index 14f1951cd0..2b7e3ebd4b 100644 --- a/sentry_sdk/integrations/django/__init__.py +++ b/sentry_sdk/integrations/django/__init__.py @@ -84,7 +84,7 @@ def is_authenticated(request_user): class DjangoIntegration(Integration): identifier = "django" - transaction_style = None + transaction_style = "" middleware_spans = None def __init__(self, transaction_style="url", middleware_spans=True): @@ -324,10 +324,12 @@ def _patch_django_asgi_handler(): def _set_transaction_name_and_source(scope, transaction_style, request): # type: (Scope, str, WSGIRequest) -> Scope try: - transaction_name = None + transaction_name = "" if transaction_style == "function_name": fn = resolve(request.path).func - transaction_name = transaction_from_function(getattr(fn, "view_class", fn)) + transaction_name = ( + transaction_from_function(getattr(fn, "view_class", fn)) or "" + ) elif transaction_style == "url": if hasattr(request, "urlconf"): @@ -344,7 +346,7 @@ def _set_transaction_name_and_source(scope, transaction_style, request): scope.set_transaction_name( transaction_name, - source=source_for_style.get(transaction_style), + source=source_for_style[transaction_style], ) except Exception: pass diff --git a/sentry_sdk/integrations/falcon.py b/sentry_sdk/integrations/falcon.py index 4bd7208094..2d0c8b76dd 100644 --- a/sentry_sdk/integrations/falcon.py +++ b/sentry_sdk/integrations/falcon.py @@ -92,7 +92,7 @@ def process_request(self, req, resp, *args, **kwargs): class FalconIntegration(Integration): identifier = "falcon" - transaction_style = None + transaction_style = "" def __init__(self, transaction_style="uri_template"): # type: (str) -> None diff --git a/sentry_sdk/integrations/flask.py b/sentry_sdk/integrations/flask.py index f15dcc02f1..b414c55cf9 100644 --- a/sentry_sdk/integrations/flask.py +++ b/sentry_sdk/integrations/flask.py @@ -28,8 +28,8 @@ try: from flask import Flask, Markup, Request # type: ignore - from flask import __version__ as FLASK_VERSION # type: ignore - from flask import _app_ctx_stack, _request_ctx_stack # type: ignore + from flask import __version__ as FLASK_VERSION + from flask import _app_ctx_stack, _request_ctx_stack from flask.signals import ( before_render_template, got_request_exception, @@ -49,7 +49,7 @@ class FlaskIntegration(Integration): identifier = "flask" - transaction_style = None + transaction_style = "" def __init__(self, transaction_style="endpoint"): # type: (str) -> None @@ -123,8 +123,8 @@ def _set_transaction_name_and_source(scope, transaction_style, request): } scope.set_transaction_name( - name_for_style.get(transaction_style), - source=source_for_style.get(transaction_style), + name_for_style[transaction_style], + source=source_for_style[transaction_style], ) except Exception: pass diff --git a/sentry_sdk/integrations/pyramid.py b/sentry_sdk/integrations/pyramid.py index 4ddbe69967..6f82e1428c 100644 --- a/sentry_sdk/integrations/pyramid.py +++ b/sentry_sdk/integrations/pyramid.py @@ -57,7 +57,7 @@ def authenticated_userid(request): class PyramidIntegration(Integration): identifier = "pyramid" - transaction_style = None + transaction_style = "" def __init__(self, transaction_style="route_name"): # type: (str) -> None @@ -170,8 +170,8 @@ def _set_transaction_name_and_source(scope, transaction_style, request): } scope.set_transaction_name( - name_for_style.get(transaction_style), - source=source_for_style.get(transaction_style), + name_for_style[transaction_style], + source=source_for_style[transaction_style], ) except Exception: pass diff --git a/sentry_sdk/integrations/quart.py b/sentry_sdk/integrations/quart.py index ee91057a44..29cc278a6d 100644 --- a/sentry_sdk/integrations/quart.py +++ b/sentry_sdk/integrations/quart.py @@ -50,7 +50,7 @@ class QuartIntegration(Integration): identifier = "quart" - transaction_style = None + transaction_style = "" def __init__(self, transaction_style="endpoint"): # type: (str) -> None @@ -99,8 +99,8 @@ def _set_transaction_name_and_source(scope, transaction_style, request): } scope.set_transaction_name( - name_for_style.get(transaction_style), - source=source_for_style.get(transaction_style), + name_for_style[transaction_style], + source=source_for_style[transaction_style], ) except Exception: pass From 31b880c6a54699e8905ed384cd5b9440ec5e969d Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 17:59:09 +0200 Subject: [PATCH 33/45] Typing fixes --- sentry_sdk/integrations/chalice.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/integrations/chalice.py b/sentry_sdk/integrations/chalice.py index ed6cdc7ce4..46b3933829 100644 --- a/sentry_sdk/integrations/chalice.py +++ b/sentry_sdk/integrations/chalice.py @@ -12,9 +12,9 @@ from sentry_sdk._types import MYPY from sentry_sdk._functools import wraps -import chalice +import chalice # type: ignore from chalice import Chalice, ChaliceViewError -from chalice.app import EventSourceHandler as ChaliceEventSourceHandler +from chalice.app import EventSourceHandler as ChaliceEventSourceHandler # type: ignore if MYPY: from typing import Any @@ -30,7 +30,7 @@ raise DidNotEnable("Chalice is not installed") -class EventSourceHandler(ChaliceEventSourceHandler): +class EventSourceHandler(ChaliceEventSourceHandler): # type: ignore def __call__(self, event, context): # type: (Any, Any) -> Any hub = Hub.current From b35dd7b72bb47549d0ecf2118aa285da9880c24c Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 18:59:22 +0200 Subject: [PATCH 34/45] Fixed Python 2.7 error --- tests/integrations/bottle/test_bottle.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/integrations/bottle/test_bottle.py b/tests/integrations/bottle/test_bottle.py index 4d4ad08dd5..0ef4339874 100644 --- a/tests/integrations/bottle/test_bottle.py +++ b/tests/integrations/bottle/test_bottle.py @@ -62,7 +62,7 @@ def test_has_context(sentry_init, app, capture_events, get_client): @pytest.mark.parametrize( "url,transaction_style,expected_transaction,expected_source", [ - ("/message", "endpoint", "test_bottle.app..hi", "component"), + ("/message", "endpoint", "hi", "component"), ("/message", "url", "/message", "route"), ("/message/123456", "url", "/message/", "route"), ("/message-named-route", "endpoint", "hi", "component"), @@ -89,7 +89,9 @@ def test_transaction_style( assert response[1] == "200 OK" (event,) = events - assert event["transaction"] == expected_transaction + # We use endswith() because in Python 2.7 it is "test_bottle.hi" + # and in later Pythons "test_bottle.app..hi" + assert event["transaction"].endswith(expected_transaction) assert event["transaction_info"] == {"source": expected_source} From 1b5d4334f65d1d8e9f9bbe8b3c382f2e5f146dac Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 20:24:35 +0200 Subject: [PATCH 35/45] Added tests for transaction source for Celery --- sentry_sdk/integrations/celery.py | 3 +-- tests/integrations/celery/test_celery.py | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/integrations/celery.py b/sentry_sdk/integrations/celery.py index f3aed57be3..bf84c10e94 100644 --- a/sentry_sdk/integrations/celery.py +++ b/sentry_sdk/integrations/celery.py @@ -158,10 +158,9 @@ def _inner(*args, **kwargs): args[3].get("headers") or {}, op="celery.task", name="unknown celery task", + source=TRANSACTION_SOURCE_TASK, ) - transaction.name = task.name - transaction.source = TRANSACTION_SOURCE_TASK transaction.set_status("ok") if transaction is None: diff --git a/tests/integrations/celery/test_celery.py b/tests/integrations/celery/test_celery.py index a77ac1adb1..951f8ecb8c 100644 --- a/tests/integrations/celery/test_celery.py +++ b/tests/integrations/celery/test_celery.py @@ -155,9 +155,11 @@ def dummy_task(x, y): assert error_event["exception"]["values"][0]["type"] == "ZeroDivisionError" execution_event, submission_event = events - assert execution_event["transaction"] == "dummy_task" + assert execution_event["transaction_info"] == {"source": "task"} + assert submission_event["transaction"] == "submission" + assert submission_event["transaction_info"] == {"source": "unknown"} assert execution_event["type"] == submission_event["type"] == "transaction" assert execution_event["contexts"]["trace"]["trace_id"] == transaction.trace_id From 33de8b8f4d4c431af05ea2eaf9a5900ead3dc8ee Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 20:45:49 +0200 Subject: [PATCH 36/45] Moved source lookup to central location to make code more dry --- sentry_sdk/integrations/aiohttp.py | 10 ++-------- sentry_sdk/integrations/bottle.py | 10 ++-------- sentry_sdk/integrations/django/__init__.py | 10 ++-------- sentry_sdk/integrations/falcon.py | 10 ++-------- sentry_sdk/integrations/flask.py | 10 ++-------- sentry_sdk/integrations/pyramid.py | 10 ++-------- sentry_sdk/integrations/quart.py | 10 ++-------- sentry_sdk/utils.py | 12 ++++++++++++ 8 files changed, 26 insertions(+), 56 deletions(-) diff --git a/sentry_sdk/integrations/aiohttp.py b/sentry_sdk/integrations/aiohttp.py index 70edee4a5d..5d849eaa6d 100644 --- a/sentry_sdk/integrations/aiohttp.py +++ b/sentry_sdk/integrations/aiohttp.py @@ -11,8 +11,7 @@ ) from sentry_sdk.tracing import Transaction from sentry_sdk.utils import ( - TRANSACTION_SOURCE_COMPONENT, - TRANSACTION_SOURCE_ROUTE, + SOURCE_FOR_STYLE, capture_internal_exceptions, event_from_exception, transaction_from_function, @@ -150,14 +149,9 @@ async def sentry_urldispatcher_resolve(self, request): if name is not None: with Hub.current.configure_scope() as scope: - source_for_style = { - "handler_name": TRANSACTION_SOURCE_COMPONENT, - "method_and_path_pattern": TRANSACTION_SOURCE_ROUTE, - } - scope.set_transaction_name( name, - source=source_for_style[integration.transaction_style], + source=SOURCE_FOR_STYLE[integration.transaction_style], ) return rv diff --git a/sentry_sdk/integrations/bottle.py b/sentry_sdk/integrations/bottle.py index 8af645d788..807c7a00ab 100644 --- a/sentry_sdk/integrations/bottle.py +++ b/sentry_sdk/integrations/bottle.py @@ -2,8 +2,7 @@ from sentry_sdk.hub import Hub from sentry_sdk.utils import ( - TRANSACTION_SOURCE_COMPONENT, - TRANSACTION_SOURCE_ROUTE, + SOURCE_FOR_STYLE, capture_internal_exceptions, event_from_exception, transaction_from_function, @@ -180,19 +179,14 @@ def size_of_file(self, file): def _set_transaction_name_and_source(event, transaction_style, request): # type: (Event, str, Any) -> Event - name_for_style = { "url": request.route.rule, "endpoint": request.route.name or transaction_from_function(request.route.callback), } - source_for_style = { - "url": TRANSACTION_SOURCE_ROUTE, - "endpoint": TRANSACTION_SOURCE_COMPONENT, - } event["transaction"] = name_for_style[transaction_style] - event["transaction_info"] = {"source": source_for_style[transaction_style]} + event["transaction_info"] = {"source": SOURCE_FOR_STYLE[transaction_style]} return event diff --git a/sentry_sdk/integrations/django/__init__.py b/sentry_sdk/integrations/django/__init__.py index 2b7e3ebd4b..0f89f28b46 100644 --- a/sentry_sdk/integrations/django/__init__.py +++ b/sentry_sdk/integrations/django/__init__.py @@ -13,8 +13,7 @@ from sentry_sdk.utils import ( HAS_REAL_CONTEXTVARS, CONTEXTVARS_ERROR_MESSAGE, - TRANSACTION_SOURCE_COMPONENT, - TRANSACTION_SOURCE_ROUTE, + SOURCE_FOR_STYLE, logger, capture_internal_exceptions, event_from_exception, @@ -339,14 +338,9 @@ def _set_transaction_name_and_source(scope, transaction_style, request): else: transaction_name = LEGACY_RESOLVER.resolve(request.path_info) - source_for_style = { - "function_name": TRANSACTION_SOURCE_COMPONENT, - "url": TRANSACTION_SOURCE_ROUTE, - } - scope.set_transaction_name( transaction_name, - source=source_for_style[transaction_style], + source=SOURCE_FOR_STYLE[transaction_style], ) except Exception: pass diff --git a/sentry_sdk/integrations/falcon.py b/sentry_sdk/integrations/falcon.py index 2d0c8b76dd..f4ccde0305 100644 --- a/sentry_sdk/integrations/falcon.py +++ b/sentry_sdk/integrations/falcon.py @@ -5,8 +5,7 @@ from sentry_sdk.integrations._wsgi_common import RequestExtractor from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware from sentry_sdk.utils import ( - TRANSACTION_SOURCE_ROUTE, - TRANSACTION_SOURCE_URL, + SOURCE_FOR_STYLE, capture_internal_exceptions, event_from_exception, ) @@ -208,13 +207,8 @@ def _set_transaction_name_and_source(event, transaction_style, request): "uri_template": request.uri_template, "path": request.path, } - source_for_style = { - "uri_template": TRANSACTION_SOURCE_ROUTE, - "path": TRANSACTION_SOURCE_URL, - } - event["transaction"] = name_for_style[transaction_style] - event["transaction_info"] = {"source": source_for_style[transaction_style]} + event["transaction_info"] = {"source": SOURCE_FOR_STYLE[transaction_style]} return event diff --git a/sentry_sdk/integrations/flask.py b/sentry_sdk/integrations/flask.py index b414c55cf9..23d667b5c6 100644 --- a/sentry_sdk/integrations/flask.py +++ b/sentry_sdk/integrations/flask.py @@ -7,8 +7,7 @@ from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware from sentry_sdk.scope import Scope from sentry_sdk.utils import ( - TRANSACTION_SOURCE_COMPONENT, - TRANSACTION_SOURCE_ROUTE, + SOURCE_FOR_STYLE, capture_internal_exceptions, event_from_exception, ) @@ -117,14 +116,9 @@ def _set_transaction_name_and_source(scope, transaction_style, request): "url": request.url_rule.rule, "endpoint": request.url_rule.endpoint, } - source_for_style = { - "url": TRANSACTION_SOURCE_ROUTE, - "endpoint": TRANSACTION_SOURCE_COMPONENT, - } - scope.set_transaction_name( name_for_style[transaction_style], - source=source_for_style[transaction_style], + source=SOURCE_FOR_STYLE[transaction_style], ) except Exception: pass diff --git a/sentry_sdk/integrations/pyramid.py b/sentry_sdk/integrations/pyramid.py index 6f82e1428c..dd883989a3 100644 --- a/sentry_sdk/integrations/pyramid.py +++ b/sentry_sdk/integrations/pyramid.py @@ -7,8 +7,7 @@ from sentry_sdk.hub import Hub, _should_send_default_pii from sentry_sdk.scope import Scope from sentry_sdk.utils import ( - TRANSACTION_SOURCE_COMPONENT, - TRANSACTION_SOURCE_ROUTE, + SOURCE_FOR_STYLE, capture_internal_exceptions, event_from_exception, ) @@ -164,14 +163,9 @@ def _set_transaction_name_and_source(scope, transaction_style, request): "route_name": request.matched_route.name, "route_pattern": request.matched_route.pattern, } - source_for_style = { - "route_name": TRANSACTION_SOURCE_COMPONENT, - "route_pattern": TRANSACTION_SOURCE_ROUTE, - } - scope.set_transaction_name( name_for_style[transaction_style], - source=source_for_style[transaction_style], + source=SOURCE_FOR_STYLE[transaction_style], ) except Exception: pass diff --git a/sentry_sdk/integrations/quart.py b/sentry_sdk/integrations/quart.py index 29cc278a6d..f250983de3 100644 --- a/sentry_sdk/integrations/quart.py +++ b/sentry_sdk/integrations/quart.py @@ -6,8 +6,7 @@ from sentry_sdk.integrations.asgi import SentryAsgiMiddleware from sentry_sdk.scope import Scope from sentry_sdk.utils import ( - TRANSACTION_SOURCE_COMPONENT, - TRANSACTION_SOURCE_ROUTE, + SOURCE_FOR_STYLE, capture_internal_exceptions, event_from_exception, ) @@ -93,14 +92,9 @@ def _set_transaction_name_and_source(scope, transaction_style, request): "url": request.url_rule.rule, "endpoint": request.url_rule.endpoint, } - source_for_style = { - "url": TRANSACTION_SOURCE_ROUTE, - "endpoint": TRANSACTION_SOURCE_COMPONENT, - } - scope.set_transaction_name( name_for_style[transaction_style], - source=source_for_style[transaction_style], + source=SOURCE_FOR_STYLE[transaction_style], ) except Exception: pass diff --git a/sentry_sdk/utils.py b/sentry_sdk/utils.py index 9f49738d1c..c62a79ca16 100644 --- a/sentry_sdk/utils.py +++ b/sentry_sdk/utils.py @@ -52,6 +52,18 @@ TRANSACTION_SOURCE_TASK = "task" TRANSACTION_SOURCE_UNKNOWN = "unknown" +SOURCE_FOR_STYLE = { + "endpoint": TRANSACTION_SOURCE_COMPONENT, + "function_name": TRANSACTION_SOURCE_COMPONENT, + "handler_name": TRANSACTION_SOURCE_COMPONENT, + "method_and_path_pattern": TRANSACTION_SOURCE_ROUTE, + "path": TRANSACTION_SOURCE_URL, + "route_name": TRANSACTION_SOURCE_COMPONENT, + "route_pattern": TRANSACTION_SOURCE_ROUTE, + "uri_template": TRANSACTION_SOURCE_ROUTE, + "url": TRANSACTION_SOURCE_ROUTE, +} + def json_dumps(data): # type: (Any) -> bytes From 510b8cb026d224ef028e909e633976229015706d Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 20:46:24 +0200 Subject: [PATCH 37/45] Refactored setting of transaction name and source --- sentry_sdk/integrations/asgi.py | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/sentry_sdk/integrations/asgi.py b/sentry_sdk/integrations/asgi.py index e3dce9b627..65e3773334 100644 --- a/sentry_sdk/integrations/asgi.py +++ b/sentry_sdk/integrations/asgi.py @@ -14,8 +14,7 @@ from sentry_sdk.integrations._wsgi_common import _filter_headers from sentry_sdk.sessions import auto_session_tracking from sentry_sdk.utils import ( - TRANSACTION_SOURCE_COMPONENT, - TRANSACTION_SOURCE_ROUTE, + SOURCE_FOR_STYLE, TRANSACTION_SOURCE_UNKNOWN, ContextVar, event_from_exception, @@ -150,6 +149,7 @@ async def _run_app(self, scope, callback): transaction = Transaction(op="asgi.server") transaction.name = _DEFAULT_TRANSACTION_NAME + transaction.source + TRANSACTION_SOURCE_UNKNOWN transaction.set_tag("asgi.type", ty) with hub.start_transaction( @@ -210,13 +210,15 @@ def _set_transaction_name_and_source(self, event, transaction_style, asgi_scope) if transaction_name_already_set: return event + name = "" + if transaction_style == "endpoint": endpoint = asgi_scope.get("endpoint") # Webframeworks like Starlette mutate the ASGI env once routing is # done, which is sometime after the request has started. If we have # an endpoint, overwrite our generic transaction name. if endpoint: - event["transaction"] = transaction_from_function(endpoint) + name = transaction_from_function(endpoint) elif transaction_style == "url": # FastAPI includes the route object in the scope to let Sentry extract the @@ -225,25 +227,16 @@ def _set_transaction_name_and_source(self, event, transaction_style, asgi_scope) if route: path = getattr(route, "path", None) if path is not None: - event["transaction"] = path - - # Set transaction source - source_for_style = { - "url": TRANSACTION_SOURCE_ROUTE, - "endpoint": TRANSACTION_SOURCE_COMPONENT, - } - event["transaction_info"] = {"source": source_for_style[transaction_style]} + name = path - transaction_name_not_set = ( - event.get("transaction", _DEFAULT_TRANSACTION_NAME) - == _DEFAULT_TRANSACTION_NAME - ) - - if transaction_name_not_set: - # If the setting of the transaction name did not work - # then set an unknown source. This can happen when ASGI frameworks - # that are not yet supported well are used. + if not name: + # If no transaction name can be found set an unknown source. + # This can happen when ASGI frameworks that are not yet supported well are used. event["transaction_info"] = {"source": TRANSACTION_SOURCE_UNKNOWN} + return event + + event["transaction"] = name + event["transaction_info"] = {"source": SOURCE_FOR_STYLE[transaction_style]} return event From e75666e7f85746c0205dcc3bd93dd856bf77f551 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Jul 2022 20:49:27 +0200 Subject: [PATCH 38/45] Small fixes --- sentry_sdk/integrations/asgi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/integrations/asgi.py b/sentry_sdk/integrations/asgi.py index 65e3773334..5514020048 100644 --- a/sentry_sdk/integrations/asgi.py +++ b/sentry_sdk/integrations/asgi.py @@ -149,7 +149,7 @@ async def _run_app(self, scope, callback): transaction = Transaction(op="asgi.server") transaction.name = _DEFAULT_TRANSACTION_NAME - transaction.source + TRANSACTION_SOURCE_UNKNOWN + transaction.source = TRANSACTION_SOURCE_UNKNOWN transaction.set_tag("asgi.type", ty) with hub.start_transaction( @@ -218,7 +218,7 @@ def _set_transaction_name_and_source(self, event, transaction_style, asgi_scope) # done, which is sometime after the request has started. If we have # an endpoint, overwrite our generic transaction name. if endpoint: - name = transaction_from_function(endpoint) + name = transaction_from_function(endpoint) or "" elif transaction_style == "url": # FastAPI includes the route object in the scope to let Sentry extract the From 6c937addecfd893cbc50bfeae3c4d50d6b2f6706 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 15 Jul 2022 11:08:06 +0200 Subject: [PATCH 39/45] Moved constants to tracing.py --- sentry_sdk/integrations/aiohttp.py | 3 +-- sentry_sdk/integrations/asgi.py | 3 +-- sentry_sdk/integrations/aws_lambda.py | 3 +-- sentry_sdk/integrations/bottle.py | 2 +- sentry_sdk/integrations/celery.py | 2 +- sentry_sdk/integrations/chalice.py | 2 +- sentry_sdk/integrations/django/__init__.py | 2 +- sentry_sdk/integrations/falcon.py | 2 +- sentry_sdk/integrations/flask.py | 2 +- sentry_sdk/integrations/gcp.py | 3 +-- sentry_sdk/integrations/pyramid.py | 2 +- sentry_sdk/integrations/quart.py | 2 +- sentry_sdk/integrations/sanic.py | 2 +- sentry_sdk/integrations/tornado.py | 3 +-- sentry_sdk/tracing.py | 25 +++++++++++++++++++++- sentry_sdk/utils.py | 22 ------------------- 16 files changed, 38 insertions(+), 42 deletions(-) diff --git a/sentry_sdk/integrations/aiohttp.py b/sentry_sdk/integrations/aiohttp.py index 5d849eaa6d..9f4a823b98 100644 --- a/sentry_sdk/integrations/aiohttp.py +++ b/sentry_sdk/integrations/aiohttp.py @@ -9,9 +9,8 @@ _filter_headers, request_body_within_bounds, ) -from sentry_sdk.tracing import Transaction +from sentry_sdk.tracing import SOURCE_FOR_STYLE, Transaction from sentry_sdk.utils import ( - SOURCE_FOR_STYLE, capture_internal_exceptions, event_from_exception, transaction_from_function, diff --git a/sentry_sdk/integrations/asgi.py b/sentry_sdk/integrations/asgi.py index 5514020048..795ee1b9a7 100644 --- a/sentry_sdk/integrations/asgi.py +++ b/sentry_sdk/integrations/asgi.py @@ -13,9 +13,8 @@ from sentry_sdk.hub import Hub, _should_send_default_pii from sentry_sdk.integrations._wsgi_common import _filter_headers from sentry_sdk.sessions import auto_session_tracking +from sentry_sdk.tracing import SOURCE_FOR_STYLE, TRANSACTION_SOURCE_UNKNOWN from sentry_sdk.utils import ( - SOURCE_FOR_STYLE, - TRANSACTION_SOURCE_UNKNOWN, ContextVar, event_from_exception, transaction_from_function, diff --git a/sentry_sdk/integrations/aws_lambda.py b/sentry_sdk/integrations/aws_lambda.py index ba28954e11..8f41ce52cb 100644 --- a/sentry_sdk/integrations/aws_lambda.py +++ b/sentry_sdk/integrations/aws_lambda.py @@ -3,10 +3,9 @@ import sys from sentry_sdk.hub import Hub, _should_send_default_pii -from sentry_sdk.tracing import Transaction +from sentry_sdk.tracing import TRANSACTION_SOURCE_COMPONENT, Transaction from sentry_sdk._compat import reraise from sentry_sdk.utils import ( - TRANSACTION_SOURCE_COMPONENT, AnnotatedValue, capture_internal_exceptions, event_from_exception, diff --git a/sentry_sdk/integrations/bottle.py b/sentry_sdk/integrations/bottle.py index 807c7a00ab..ff5462001d 100644 --- a/sentry_sdk/integrations/bottle.py +++ b/sentry_sdk/integrations/bottle.py @@ -1,8 +1,8 @@ from __future__ import absolute_import from sentry_sdk.hub import Hub +from sentry_sdk.tracing import SOURCE_FOR_STYLE from sentry_sdk.utils import ( - SOURCE_FOR_STYLE, capture_internal_exceptions, event_from_exception, transaction_from_function, diff --git a/sentry_sdk/integrations/celery.py b/sentry_sdk/integrations/celery.py index bf84c10e94..2a095ec8c6 100644 --- a/sentry_sdk/integrations/celery.py +++ b/sentry_sdk/integrations/celery.py @@ -3,8 +3,8 @@ import sys from sentry_sdk.hub import Hub +from sentry_sdk.tracing import TRANSACTION_SOURCE_TASK from sentry_sdk.utils import ( - TRANSACTION_SOURCE_TASK, capture_internal_exceptions, event_from_exception, ) diff --git a/sentry_sdk/integrations/chalice.py b/sentry_sdk/integrations/chalice.py index 46b3933829..80069b2951 100644 --- a/sentry_sdk/integrations/chalice.py +++ b/sentry_sdk/integrations/chalice.py @@ -4,8 +4,8 @@ from sentry_sdk.hub import Hub from sentry_sdk.integrations import Integration, DidNotEnable from sentry_sdk.integrations.aws_lambda import _make_request_event_processor +from sentry_sdk.tracing import TRANSACTION_SOURCE_COMPONENT from sentry_sdk.utils import ( - TRANSACTION_SOURCE_COMPONENT, capture_internal_exceptions, event_from_exception, ) diff --git a/sentry_sdk/integrations/django/__init__.py b/sentry_sdk/integrations/django/__init__.py index 0f89f28b46..4edc4ce852 100644 --- a/sentry_sdk/integrations/django/__init__.py +++ b/sentry_sdk/integrations/django/__init__.py @@ -9,11 +9,11 @@ from sentry_sdk.hub import Hub, _should_send_default_pii from sentry_sdk.scope import add_global_event_processor from sentry_sdk.serializer import add_global_repr_processor +from sentry_sdk.tracing import SOURCE_FOR_STYLE from sentry_sdk.tracing_utils import record_sql_queries from sentry_sdk.utils import ( HAS_REAL_CONTEXTVARS, CONTEXTVARS_ERROR_MESSAGE, - SOURCE_FOR_STYLE, logger, capture_internal_exceptions, event_from_exception, diff --git a/sentry_sdk/integrations/falcon.py b/sentry_sdk/integrations/falcon.py index f4ccde0305..6b0a0feac3 100644 --- a/sentry_sdk/integrations/falcon.py +++ b/sentry_sdk/integrations/falcon.py @@ -4,8 +4,8 @@ from sentry_sdk.integrations import Integration, DidNotEnable from sentry_sdk.integrations._wsgi_common import RequestExtractor from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware +from sentry_sdk.tracing import SOURCE_FOR_STYLE from sentry_sdk.utils import ( - SOURCE_FOR_STYLE, capture_internal_exceptions, event_from_exception, ) diff --git a/sentry_sdk/integrations/flask.py b/sentry_sdk/integrations/flask.py index 23d667b5c6..e61483b3d9 100644 --- a/sentry_sdk/integrations/flask.py +++ b/sentry_sdk/integrations/flask.py @@ -6,8 +6,8 @@ from sentry_sdk.integrations._wsgi_common import RequestExtractor from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware from sentry_sdk.scope import Scope +from sentry_sdk.tracing import SOURCE_FOR_STYLE from sentry_sdk.utils import ( - SOURCE_FOR_STYLE, capture_internal_exceptions, event_from_exception, ) diff --git a/sentry_sdk/integrations/gcp.py b/sentry_sdk/integrations/gcp.py index 9cc6d42597..e401daa9ca 100644 --- a/sentry_sdk/integrations/gcp.py +++ b/sentry_sdk/integrations/gcp.py @@ -3,10 +3,9 @@ import sys from sentry_sdk.hub import Hub, _should_send_default_pii -from sentry_sdk.tracing import Transaction +from sentry_sdk.tracing import TRANSACTION_SOURCE_COMPONENT, Transaction from sentry_sdk._compat import reraise from sentry_sdk.utils import ( - TRANSACTION_SOURCE_COMPONENT, AnnotatedValue, capture_internal_exceptions, event_from_exception, diff --git a/sentry_sdk/integrations/pyramid.py b/sentry_sdk/integrations/pyramid.py index dd883989a3..353470c7ce 100644 --- a/sentry_sdk/integrations/pyramid.py +++ b/sentry_sdk/integrations/pyramid.py @@ -6,8 +6,8 @@ from sentry_sdk.hub import Hub, _should_send_default_pii from sentry_sdk.scope import Scope +from sentry_sdk.tracing import SOURCE_FOR_STYLE from sentry_sdk.utils import ( - SOURCE_FOR_STYLE, capture_internal_exceptions, event_from_exception, ) diff --git a/sentry_sdk/integrations/quart.py b/sentry_sdk/integrations/quart.py index f250983de3..c6b76d9aad 100644 --- a/sentry_sdk/integrations/quart.py +++ b/sentry_sdk/integrations/quart.py @@ -5,8 +5,8 @@ from sentry_sdk.integrations._wsgi_common import _filter_headers from sentry_sdk.integrations.asgi import SentryAsgiMiddleware from sentry_sdk.scope import Scope +from sentry_sdk.tracing import SOURCE_FOR_STYLE from sentry_sdk.utils import ( - SOURCE_FOR_STYLE, capture_internal_exceptions, event_from_exception, ) diff --git a/sentry_sdk/integrations/sanic.py b/sentry_sdk/integrations/sanic.py index f54bedd630..8892f93ed7 100644 --- a/sentry_sdk/integrations/sanic.py +++ b/sentry_sdk/integrations/sanic.py @@ -4,8 +4,8 @@ from sentry_sdk._compat import urlparse, reraise from sentry_sdk.hub import Hub +from sentry_sdk.tracing import TRANSACTION_SOURCE_COMPONENT from sentry_sdk.utils import ( - TRANSACTION_SOURCE_COMPONENT, capture_internal_exceptions, event_from_exception, HAS_REAL_CONTEXTVARS, diff --git a/sentry_sdk/integrations/tornado.py b/sentry_sdk/integrations/tornado.py index 60a3bda643..af048fb5e0 100644 --- a/sentry_sdk/integrations/tornado.py +++ b/sentry_sdk/integrations/tornado.py @@ -3,11 +3,10 @@ from inspect import iscoroutinefunction from sentry_sdk.hub import Hub, _should_send_default_pii -from sentry_sdk.tracing import Transaction +from sentry_sdk.tracing import TRANSACTION_SOURCE_COMPONENT, Transaction from sentry_sdk.utils import ( HAS_REAL_CONTEXTVARS, CONTEXTVARS_ERROR_MESSAGE, - TRANSACTION_SOURCE_COMPONENT, event_from_exception, capture_internal_exceptions, transaction_from_function, diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index ad1cd20f68..f77aa1ea64 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -6,7 +6,7 @@ import sentry_sdk -from sentry_sdk.utils import TRANSACTION_SOURCE_UNKNOWN, logger +from sentry_sdk.utils import logger from sentry_sdk._types import MYPY @@ -23,6 +23,29 @@ from sentry_sdk._types import SamplingContext, MeasurementUnit +# Transaction source +# see https://develop.sentry.dev/sdk/event-payloads/transaction/#transaction-annotations +TRANSACTION_SOURCE_CUSTOM = "custom" +TRANSACTION_SOURCE_URL = "url" +TRANSACTION_SOURCE_ROUTE = "route" +TRANSACTION_SOURCE_VIEW = "view" +TRANSACTION_SOURCE_COMPONENT = "component" +TRANSACTION_SOURCE_TASK = "task" +TRANSACTION_SOURCE_UNKNOWN = "unknown" + +SOURCE_FOR_STYLE = { + "endpoint": TRANSACTION_SOURCE_COMPONENT, + "function_name": TRANSACTION_SOURCE_COMPONENT, + "handler_name": TRANSACTION_SOURCE_COMPONENT, + "method_and_path_pattern": TRANSACTION_SOURCE_ROUTE, + "path": TRANSACTION_SOURCE_URL, + "route_name": TRANSACTION_SOURCE_COMPONENT, + "route_pattern": TRANSACTION_SOURCE_ROUTE, + "uri_template": TRANSACTION_SOURCE_ROUTE, + "url": TRANSACTION_SOURCE_ROUTE, +} + + class _SpanRecorder(object): """Limits the number of spans recorded in a transaction.""" diff --git a/sentry_sdk/utils.py b/sentry_sdk/utils.py index c62a79ca16..38ba4d7857 100644 --- a/sentry_sdk/utils.py +++ b/sentry_sdk/utils.py @@ -42,28 +42,6 @@ MAX_STRING_LENGTH = 512 BASE64_ALPHABET = re.compile(r"^[a-zA-Z0-9/+=]*$") -# Transaction source -# see https://develop.sentry.dev/sdk/event-payloads/transaction/#transaction-annotations -TRANSACTION_SOURCE_CUSTOM = "custom" -TRANSACTION_SOURCE_URL = "url" -TRANSACTION_SOURCE_ROUTE = "route" -TRANSACTION_SOURCE_VIEW = "view" -TRANSACTION_SOURCE_COMPONENT = "component" -TRANSACTION_SOURCE_TASK = "task" -TRANSACTION_SOURCE_UNKNOWN = "unknown" - -SOURCE_FOR_STYLE = { - "endpoint": TRANSACTION_SOURCE_COMPONENT, - "function_name": TRANSACTION_SOURCE_COMPONENT, - "handler_name": TRANSACTION_SOURCE_COMPONENT, - "method_and_path_pattern": TRANSACTION_SOURCE_ROUTE, - "path": TRANSACTION_SOURCE_URL, - "route_name": TRANSACTION_SOURCE_COMPONENT, - "route_pattern": TRANSACTION_SOURCE_ROUTE, - "uri_template": TRANSACTION_SOURCE_ROUTE, - "url": TRANSACTION_SOURCE_ROUTE, -} - def json_dumps(data): # type: (Any) -> bytes From 3fbd623536669805507e7a8ad50323d226997103 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 15 Jul 2022 11:10:25 +0200 Subject: [PATCH 40/45] Moved new parameter to end of parameter list for backwards compatibility --- sentry_sdk/tracing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index f77aa1ea64..dd4b1a730d 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -537,11 +537,11 @@ class Transaction(Span): def __init__( self, name="", # type: str - source=TRANSACTION_SOURCE_UNKNOWN, # type: str parent_sampled=None, # type: Optional[bool] sentry_tracestate=None, # type: Optional[str] third_party_tracestate=None, # type: Optional[str] baggage=None, # type: Optional[Baggage] + source=TRANSACTION_SOURCE_UNKNOWN, # type: str **kwargs # type: Any ): # type: (...) -> None From 37c27452efde796720a70fc56bd6d4c4bd9b97e9 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 15 Jul 2022 11:12:14 +0200 Subject: [PATCH 41/45] Changed default to be 'route' so relay does not drop it --- sentry_sdk/integrations/asgi.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/integrations/asgi.py b/sentry_sdk/integrations/asgi.py index 795ee1b9a7..4a6325ae02 100644 --- a/sentry_sdk/integrations/asgi.py +++ b/sentry_sdk/integrations/asgi.py @@ -13,7 +13,11 @@ from sentry_sdk.hub import Hub, _should_send_default_pii from sentry_sdk.integrations._wsgi_common import _filter_headers from sentry_sdk.sessions import auto_session_tracking -from sentry_sdk.tracing import SOURCE_FOR_STYLE, TRANSACTION_SOURCE_UNKNOWN +from sentry_sdk.tracing import ( + SOURCE_FOR_STYLE, + TRANSACTION_SOURCE_ROUTE, + TRANSACTION_SOURCE_UNKNOWN, +) from sentry_sdk.utils import ( ContextVar, event_from_exception, @@ -148,7 +152,7 @@ async def _run_app(self, scope, callback): transaction = Transaction(op="asgi.server") transaction.name = _DEFAULT_TRANSACTION_NAME - transaction.source = TRANSACTION_SOURCE_UNKNOWN + transaction.source = TRANSACTION_SOURCE_ROUTE transaction.set_tag("asgi.type", ty) with hub.start_transaction( From 4fae0c7cdc3dad677ae5fc1706f43b3a17c8b0eb Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 15 Jul 2022 11:32:32 +0200 Subject: [PATCH 42/45] Removed return for in-place edited object. --- sentry_sdk/integrations/asgi.py | 8 ++------ sentry_sdk/integrations/bottle.py | 10 +++------- sentry_sdk/integrations/django/__init__.py | 8 ++------ sentry_sdk/integrations/falcon.py | 8 ++------ sentry_sdk/integrations/flask.py | 8 ++------ sentry_sdk/integrations/pyramid.py | 6 ++---- sentry_sdk/integrations/quart.py | 6 ++---- 7 files changed, 15 insertions(+), 39 deletions(-) diff --git a/sentry_sdk/integrations/asgi.py b/sentry_sdk/integrations/asgi.py index 4a6325ae02..7603353ec9 100644 --- a/sentry_sdk/integrations/asgi.py +++ b/sentry_sdk/integrations/asgi.py @@ -189,9 +189,7 @@ def event_processor(self, event, hint, asgi_scope): if client and _should_send_default_pii(): request_info["env"] = {"REMOTE_ADDR": self._get_ip(asgi_scope)} - event = self._set_transaction_name_and_source( - event, self.transaction_style, asgi_scope - ) + self._set_transaction_name_and_source(event, self.transaction_style, asgi_scope) event["request"] = request_info @@ -204,7 +202,7 @@ def event_processor(self, event, hint, asgi_scope): # for that. def _set_transaction_name_and_source(self, event, transaction_style, asgi_scope): - # type: (Event, str, Any) -> Event + # type: (Event, str, Any) -> None transaction_name_already_set = ( event.get("transaction", _DEFAULT_TRANSACTION_NAME) @@ -241,8 +239,6 @@ def _set_transaction_name_and_source(self, event, transaction_style, asgi_scope) event["transaction"] = name event["transaction_info"] = {"source": SOURCE_FOR_STYLE[transaction_style]} - return event - def _get_url(self, scope, default_scheme, host): # type: (Dict[str, Any], Literal["ws", "http"], Optional[str]) -> str """ diff --git a/sentry_sdk/integrations/bottle.py b/sentry_sdk/integrations/bottle.py index ff5462001d..05edfa1120 100644 --- a/sentry_sdk/integrations/bottle.py +++ b/sentry_sdk/integrations/bottle.py @@ -178,7 +178,7 @@ def size_of_file(self, file): def _set_transaction_name_and_source(event, transaction_style, request): - # type: (Event, str, Any) -> Event + # type: (Event, str, Any) -> None name_for_style = { "url": request.route.rule, "endpoint": request.route.name @@ -188,17 +188,13 @@ def _set_transaction_name_and_source(event, transaction_style, request): event["transaction"] = name_for_style[transaction_style] event["transaction_info"] = {"source": SOURCE_FOR_STYLE[transaction_style]} - return event - def _make_request_event_processor(app, request, integration): # type: (Bottle, LocalRequest, BottleIntegration) -> EventProcessor + def event_processor(event, hint): # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any] - - event = _set_transaction_name_and_source( - event, integration.transaction_style, request - ) + _set_transaction_name_and_source(event, integration.transaction_style, request) with capture_internal_exceptions(): BottleRequestExtractor(request).extract_into_event(event) diff --git a/sentry_sdk/integrations/django/__init__.py b/sentry_sdk/integrations/django/__init__.py index 4edc4ce852..6bd1dd2c0b 100644 --- a/sentry_sdk/integrations/django/__init__.py +++ b/sentry_sdk/integrations/django/__init__.py @@ -321,7 +321,7 @@ def _patch_django_asgi_handler(): def _set_transaction_name_and_source(scope, transaction_style, request): - # type: (Scope, str, WSGIRequest) -> Scope + # type: (Scope, str, WSGIRequest) -> None try: transaction_name = "" if transaction_style == "function_name": @@ -345,8 +345,6 @@ def _set_transaction_name_and_source(scope, transaction_style, request): except Exception: pass - return scope - def _before_get_response(request): # type: (WSGIRequest) -> None @@ -359,9 +357,7 @@ def _before_get_response(request): with hub.configure_scope() as scope: # Rely on WSGI middleware to start a trace - scope = _set_transaction_name_and_source( - scope, integration.transaction_style, request - ) + _set_transaction_name_and_source(scope, integration.transaction_style, request) scope.add_event_processor( _make_event_processor(weakref.ref(request), integration) diff --git a/sentry_sdk/integrations/falcon.py b/sentry_sdk/integrations/falcon.py index 6b0a0feac3..b38e4bd5b4 100644 --- a/sentry_sdk/integrations/falcon.py +++ b/sentry_sdk/integrations/falcon.py @@ -202,7 +202,7 @@ def _exception_leads_to_http_5xx(ex): def _set_transaction_name_and_source(event, transaction_style, request): - # type: (Dict[str, Any], str, falcon.Request) -> Dict[str, Any] + # type: (Dict[str, Any], str, falcon.Request) -> None name_for_style = { "uri_template": request.uri_template, "path": request.path, @@ -210,17 +210,13 @@ def _set_transaction_name_and_source(event, transaction_style, request): event["transaction"] = name_for_style[transaction_style] event["transaction_info"] = {"source": SOURCE_FOR_STYLE[transaction_style]} - return event - def _make_request_event_processor(req, integration): # type: (falcon.Request, FalconIntegration) -> EventProcessor def event_processor(event, hint): # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any] - event = _set_transaction_name_and_source( - event, integration.transaction_style, req - ) + _set_transaction_name_and_source(event, integration.transaction_style, req) with capture_internal_exceptions(): FalconRequestExtractor(req).extract_into_event(event) diff --git a/sentry_sdk/integrations/flask.py b/sentry_sdk/integrations/flask.py index e61483b3d9..0aa8d2f120 100644 --- a/sentry_sdk/integrations/flask.py +++ b/sentry_sdk/integrations/flask.py @@ -110,7 +110,7 @@ def _add_sentry_trace(sender, template, context, **extra): def _set_transaction_name_and_source(scope, transaction_style, request): - # type: (Scope, str, Request) -> Scope + # type: (Scope, str, Request) -> None try: name_for_style = { "url": request.url_rule.rule, @@ -123,8 +123,6 @@ def _set_transaction_name_and_source(scope, transaction_style, request): except Exception: pass - return scope - def _request_started(sender, **kwargs): # type: (Flask, **Any) -> None @@ -139,9 +137,7 @@ def _request_started(sender, **kwargs): # Set the transaction name and source here, # but rely on WSGI middleware to actually start the transaction - scope = _set_transaction_name_and_source( - scope, integration.transaction_style, request - ) + _set_transaction_name_and_source(scope, integration.transaction_style, request) evt_processor = _make_request_event_processor(app, request, integration) scope.add_event_processor(evt_processor) diff --git a/sentry_sdk/integrations/pyramid.py b/sentry_sdk/integrations/pyramid.py index 353470c7ce..1e234fcffd 100644 --- a/sentry_sdk/integrations/pyramid.py +++ b/sentry_sdk/integrations/pyramid.py @@ -81,7 +81,7 @@ def sentry_patched_call_view(registry, request, *args, **kwargs): if integration is not None: with hub.configure_scope() as scope: - scope = _set_transaction_name_and_source( + _set_transaction_name_and_source( scope, integration.transaction_style, request ) scope.add_event_processor( @@ -157,7 +157,7 @@ def _capture_exception(exc_info): def _set_transaction_name_and_source(scope, transaction_style, request): - # type: (Scope, str, Request) -> Scope + # type: (Scope, str, Request) -> None try: name_for_style = { "route_name": request.matched_route.name, @@ -170,8 +170,6 @@ def _set_transaction_name_and_source(scope, transaction_style, request): except Exception: pass - return scope - class PyramidRequestExtractor(RequestExtractor): def url(self): diff --git a/sentry_sdk/integrations/quart.py b/sentry_sdk/integrations/quart.py index c6b76d9aad..1ccd982d0e 100644 --- a/sentry_sdk/integrations/quart.py +++ b/sentry_sdk/integrations/quart.py @@ -85,7 +85,7 @@ async def sentry_patched_asgi_app(self, scope, receive, send): def _set_transaction_name_and_source(scope, transaction_style, request): - # type: (Scope, str, Request) -> Scope + # type: (Scope, str, Request) -> None try: name_for_style = { @@ -99,8 +99,6 @@ def _set_transaction_name_and_source(scope, transaction_style, request): except Exception: pass - return scope - def _request_websocket_started(sender, **kwargs): # type: (Quart, **Any) -> None @@ -118,7 +116,7 @@ def _request_websocket_started(sender, **kwargs): # Set the transaction name here, but rely on ASGI middleware # to actually start the transaction - scope = _set_transaction_name_and_source( + _set_transaction_name_and_source( scope, integration.transaction_style, request_websocket ) From f3ba630a26b42b7651cb80db68bdd046e5432f34 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 15 Jul 2022 11:36:11 +0200 Subject: [PATCH 43/45] Performance optimisation :-) --- sentry_sdk/integrations/bottle.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sentry_sdk/integrations/bottle.py b/sentry_sdk/integrations/bottle.py index 05edfa1120..df3c7b1add 100644 --- a/sentry_sdk/integrations/bottle.py +++ b/sentry_sdk/integrations/bottle.py @@ -179,13 +179,15 @@ def size_of_file(self, file): def _set_transaction_name_and_source(event, transaction_style, request): # type: (Event, str, Any) -> None - name_for_style = { - "url": request.route.rule, - "endpoint": request.route.name - or transaction_from_function(request.route.callback), - } + name = "" - event["transaction"] = name_for_style[transaction_style] + if transaction_style == "url": + name = request.route.rule + + elif transaction_style == "endpoint": + name = request.route.name or transaction_from_function(request.route.callback) + + event["transaction"] = name event["transaction_info"] = {"source": SOURCE_FOR_STYLE[transaction_style]} From fac86523eb544aa47416efa79670d8c1223b448c Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 15 Jul 2022 11:40:06 +0200 Subject: [PATCH 44/45] Fixed types --- sentry_sdk/integrations/asgi.py | 4 ++-- sentry_sdk/integrations/bottle.py | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/sentry_sdk/integrations/asgi.py b/sentry_sdk/integrations/asgi.py index 7603353ec9..3aa9fcb572 100644 --- a/sentry_sdk/integrations/asgi.py +++ b/sentry_sdk/integrations/asgi.py @@ -209,7 +209,7 @@ def _set_transaction_name_and_source(self, event, transaction_style, asgi_scope) != _DEFAULT_TRANSACTION_NAME ) if transaction_name_already_set: - return event + return name = "" @@ -234,7 +234,7 @@ def _set_transaction_name_and_source(self, event, transaction_style, asgi_scope) # If no transaction name can be found set an unknown source. # This can happen when ASGI frameworks that are not yet supported well are used. event["transaction_info"] = {"source": TRANSACTION_SOURCE_UNKNOWN} - return event + return event["transaction"] = name event["transaction_info"] = {"source": SOURCE_FOR_STYLE[transaction_style]} diff --git a/sentry_sdk/integrations/bottle.py b/sentry_sdk/integrations/bottle.py index df3c7b1add..271fc150b1 100644 --- a/sentry_sdk/integrations/bottle.py +++ b/sentry_sdk/integrations/bottle.py @@ -182,10 +182,14 @@ def _set_transaction_name_and_source(event, transaction_style, request): name = "" if transaction_style == "url": - name = request.route.rule + name = request.route.rule or "" elif transaction_style == "endpoint": - name = request.route.name or transaction_from_function(request.route.callback) + name = ( + request.route.name + or transaction_from_function(request.route.callback) + or "" + ) event["transaction"] = name event["transaction_info"] = {"source": SOURCE_FOR_STYLE[transaction_style]} From f919d9d040650ed7e1962989744b094a20da07dc Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 15 Jul 2022 12:29:53 +0200 Subject: [PATCH 45/45] Marked async fixture (because we set strict asyncio_mode in pytest.ini --- tests/integrations/quart/test_quart.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/integrations/quart/test_quart.py b/tests/integrations/quart/test_quart.py index 0665209570..6d2c590a53 100644 --- a/tests/integrations/quart/test_quart.py +++ b/tests/integrations/quart/test_quart.py @@ -1,4 +1,5 @@ import pytest +import pytest_asyncio quart = pytest.importorskip("quart") @@ -21,7 +22,7 @@ auth_manager = AuthManager() -@pytest.fixture +@pytest_asyncio.fixture async def app(): app = Quart(__name__) app.debug = True