diff --git a/README.md b/README.md index fa1654e..2598e71 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,7 @@ Install the package using your favorite packaging tool: pip / poetry / pdm, etc. - `APM_REQUEST_SAVE_QUERY_PARAMETERS`: Boolean that when set to `True` will save the request query parameters as json. Defaults to `True`; - `APM_REQUEST_SAVE_QUERY_STRING`: Boolean that when set to `True` will save the request raw query string. Defaults to `True`; - `APM_NOTIFY_USING_CELERY`: Boolean that when set to `True` will dispatch a celery task when notificating. Since the process of notificating Integration can take a long time, we suggest you to set this to `True`. Defaults to `False`. + - `APM_NOTIFY_ON_DEBUG_TRUE`: Boolean that when set to `True` will notify errors even when `DEBUG=True`. Defaults to `False`. ## Storage considerations diff --git a/djapm/apm/dflt_conf.py b/djapm/apm/dflt_conf.py index a89cd93..53a0702 100644 --- a/djapm/apm/dflt_conf.py +++ b/djapm/apm/dflt_conf.py @@ -5,3 +5,4 @@ APM_REQUEST_SAVE_QUERY_STRING = True APM_NOTIFY_USING_CELERY = False +APM_NOTIFY_ON_DEBUG_TRUE = False diff --git a/djapm/apm/middlewares.py b/djapm/apm/middlewares.py index 9e1b0ba..2c9bccf 100644 --- a/djapm/apm/middlewares.py +++ b/djapm/apm/middlewares.py @@ -2,6 +2,7 @@ import logging from time import perf_counter import traceback +import warnings from django.conf import settings from django.http import HttpRequest @@ -113,6 +114,15 @@ def process_exception( # This request was not processed by the decorator `apm_api_view` return trace = self._register_error_trace(request, exception) + notify_on_debug_true = getattr( + settings, "APM_NOTIFY_ON_DEBUG_TRUE", dflt_conf.APM_NOTIFY_ON_DEBUG_TRUE + ) + if settings.DEBUG and not notify_on_debug_true: + warnings.warn( + "Errors Notifications aren't sent when DEBUG=True and APM_NOTIFY_ON_DEBUG_TRUE=False" + ) + return + use_celery = getattr( settings, "APM_NOTIFY_USING_CELERY", dflt_conf.APM_NOTIFY_USING_CELERY ) diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..58c37e7 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,30 @@ +from typing import Any + +from django.test.client import RequestFactory +import pytest + +from djapm.apm.contrib import _contribute_to_request +from djapm.apm.types import ApmRequest, PatchedHttpRequest + +from tests.types import ApmRequestFactory + + +@pytest.fixture +def apm_rf(rf: RequestFactory, admin_user) -> ApmRequestFactory: + """Request factory for a APM view, that calls `_contribute_to_request`""" + + def wrapper( + method: str, url: str, view: Any, drf_req: bool = False, user=None + ) -> PatchedHttpRequest: + func = getattr(rf, method.lower()) + req = func(url) + req.user = user or admin_user + + rest_req = None + if drf_req: + rest_req = ApmRequest(req) + + _contribute_to_request(req, view=view, logger_name=None, rest_request=rest_req) + return req + + return wrapper diff --git a/tests/test_apm/test_contrib/test_view_name.py b/tests/test_apm/test_contrib/test_view_name.py index 5c0a3b6..ffe44db 100644 --- a/tests/test_apm/test_contrib/test_view_name.py +++ b/tests/test_apm/test_contrib/test_view_name.py @@ -1,22 +1,21 @@ import pytest -from django.test.client import RequestFactory from django.urls import reverse from rest_framework.request import Request -from djapm.apm.contrib import _contribute_to_request from polls.views import OrderedPolls, get_polls_page, get_polls +from tests.types import ApmRequestFactory -def test_contribute_to_request_on_regular_class_based_view(rf: RequestFactory): - request = rf.get(reverse("polls-page-list-cbv")) - _contribute_to_request(request, view=OrderedPolls.as_view(), logger_name=None) +def test_contribute_to_request_on_regular_class_based_view(apm_rf: ApmRequestFactory): + request = apm_rf("GET", reverse("polls-page-list-cbv"), OrderedPolls.as_view()) assert request.view_name == "polls.dj.OrderedPolls" -def test_contribute_to_request_on_regular_function_based_view(rf: RequestFactory): - request = rf.get(reverse("polls-page-list")) - _contribute_to_request(request, view=get_polls_page, logger_name=None) +def test_contribute_to_request_on_regular_function_based_view( + apm_rf: ApmRequestFactory, +): + request = apm_rf("GET", reverse("polls-page-list"), get_polls_page) assert request.view_name == "polls.dj.get_polls_page" @@ -25,9 +24,6 @@ def test_contribute_to_request_on_drf_class_based_view(): assert False -def test_contribute_to_request_on_drf_function_based_view(rf: RequestFactory): - request = rf.get(reverse("polls-list")) - _contribute_to_request( - request, view=get_polls, logger_name=None, rest_request=Request(request) - ) +def test_contribute_to_request_on_drf_function_based_view(apm_rf: ApmRequestFactory): + request = apm_rf("GET", reverse("polls-list"), get_polls, drf_req=True) assert request.view_name == "polls.drf.get_polls" diff --git a/tests/test_apm/test_middlewares/test_error_trace_middleware.py b/tests/test_apm/test_middlewares/test_error_trace_middleware.py new file mode 100644 index 0000000..0c2bd76 --- /dev/null +++ b/tests/test_apm/test_middlewares/test_error_trace_middleware.py @@ -0,0 +1,22 @@ +import warnings + +from django.urls import reverse + +from djapm.apm.middlewares import ErrorTraceMiddleware +from polls.views import get_polls +from tests.types import ApmRequestFactory + + +def test_process_exception_warns_when_debug_true_and_notify_on_debug_true_is_false( + settings, apm_rf: ApmRequestFactory +): + settings.DEBUG = True + settings.APM_NOTIFY_ON_DEBUG_TRUE = False + + middleware = ErrorTraceMiddleware(lambda r: r) + + with warnings.catch_warnings(record=True) as w: + request = apm_rf("GET", reverse("polls-list"), get_polls, drf_req=True) + middleware.process_exception(request, ValueError("Oops, A exception occurred")) + + assert len(w) == 1, "No warnings were emitted" diff --git a/tests/types.py b/tests/types.py new file mode 100644 index 0000000..2696a65 --- /dev/null +++ b/tests/types.py @@ -0,0 +1,10 @@ +from typing import Any, Protocol + +from djapm.apm.types import PatchedHttpRequest + + +class ApmRequestFactory(Protocol): + def __call__( + self, method: str, url: str, view: Any, drf_req: bool = False, user=None + ) -> PatchedHttpRequest: + ...