diff --git a/sentry_sdk/integrations/django/signals_handlers.py b/sentry_sdk/integrations/django/signals_handlers.py index 71bc07f854..4d81772452 100644 --- a/sentry_sdk/integrations/django/signals_handlers.py +++ b/sentry_sdk/integrations/django/signals_handlers.py @@ -13,19 +13,32 @@ from typing import List +def _get_receiver_name(receiver): + # type: (Callable[..., Any]) -> str + name = "" + + if hasattr(receiver, "__qualname__"): + name += receiver.__qualname__ + elif hasattr(receiver, "__name__"): # Python 2.7 has no __qualname__ + name += receiver.__name__ + + if ( + name == "" + ): # certain functions (like partials) dont have a name so return the string representation + return str(receiver) + + if hasattr(receiver, "__module__"): # prepend with module, if there is one + name = receiver.__module__ + "." + name + + return name + + def patch_signals(): # type: () -> None """Patch django signal receivers to create a span""" old_live_receivers = Signal._live_receivers - def _get_receiver_name(receiver): - # type: (Callable[..., Any]) -> str - name = receiver.__module__ + "." - if hasattr(receiver, "__name__"): - return name + receiver.__name__ - return name + str(receiver) - def _sentry_live_receivers(self, sender): # type: (Signal, Any) -> List[Callable[..., Any]] hub = Hub.current @@ -35,11 +48,12 @@ def sentry_receiver_wrapper(receiver): # type: (Callable[..., Any]) -> Callable[..., Any] def wrapper(*args, **kwargs): # type: (Any, Any) -> Any + signal_name = _get_receiver_name(receiver) with hub.start_span( op="django.signals", - description=_get_receiver_name(receiver), + description=signal_name, ) as span: - span.set_data("signal", _get_receiver_name(receiver)) + span.set_data("signal", signal_name) return receiver(*args, **kwargs) return wrapper diff --git a/tests/integrations/django/test_basic.py b/tests/integrations/django/test_basic.py index b1fee30e2c..7809239c30 100644 --- a/tests/integrations/django/test_basic.py +++ b/tests/integrations/django/test_basic.py @@ -1,8 +1,9 @@ from __future__ import absolute_import +import json import pytest import pytest_django -import json +from functools import partial from werkzeug.test import Client from django import VERSION as DJANGO_VERSION @@ -10,16 +11,16 @@ from django.core.management import execute_from_command_line from django.db.utils import OperationalError, ProgrammingError, DataError -from sentry_sdk.integrations.executing import ExecutingIntegration - try: from django.urls import reverse except ImportError: from django.core.urlresolvers import reverse +from sentry_sdk._compat import PY2 from sentry_sdk import capture_message, capture_exception, configure_scope from sentry_sdk.integrations.django import DjangoIntegration -from functools import partial +from sentry_sdk.integrations.django.signals_handlers import _get_receiver_name +from sentry_sdk.integrations.executing import ExecutingIntegration from tests.integrations.django.myapp.wsgi import application @@ -816,3 +817,22 @@ def test_custom_urlconf_middleware( assert "custom_urlconf_middleware" in render_span_tree(transaction_event) settings.MIDDLEWARE.pop(0) + + +def test_get_receiver_name(): + def dummy(a, b): + return a + b + + name = _get_receiver_name(dummy) + + if PY2: + assert name == "tests.integrations.django.test_basic.dummy" + else: + assert ( + name + == "tests.integrations.django.test_basic.test_get_receiver_name..dummy" + ) + + a_partial = partial(dummy) + name = _get_receiver_name(a_partial) + assert name == str(a_partial)