diff --git a/debug_toolbar/settings.py b/debug_toolbar/settings.py index fcd253c59..a7d49adb5 100644 --- a/debug_toolbar/settings.py +++ b/debug_toolbar/settings.py @@ -37,6 +37,7 @@ "PROFILER_CAPTURE_PROJECT_CODE": True, "PROFILER_MAX_DEPTH": 10, "PROFILER_THRESHOLD_RATIO": 8, + "SERIALIZATION_WARNINGS": True, "SHOW_TEMPLATE_CONTEXT": True, "SKIP_TEMPLATE_PREFIXES": ("django/forms/widgets/", "admin/widgets/"), "SQL_WARNING_THRESHOLD": 500, # milliseconds diff --git a/debug_toolbar/store.py b/debug_toolbar/store.py index b32d3b62a..4344431b0 100644 --- a/debug_toolbar/store.py +++ b/debug_toolbar/store.py @@ -1,5 +1,6 @@ import contextlib import json +import logging from collections import defaultdict, deque from typing import Any, Dict, Iterable @@ -8,6 +9,8 @@ from debug_toolbar import settings as dt_settings +logger = logging.getLogger(__name__) + def serialize(data: Any) -> str: # If this starts throwing an exceptions, consider @@ -103,7 +106,14 @@ def delete(cls, request_id: str): def save_panel(cls, request_id: str, panel_id: str, data: Any = None): """Save the panel data for the given request_id""" cls.set(request_id) - cls._request_store[request_id][panel_id] = serialize(data) + try: + cls._request_store[request_id][panel_id] = serialize(data) + except TypeError: + if dt_settings.get_config()["SERIALIZATION_WARNINGS"]: + log = "Panel (%s) failed to serialized data %s properly." + logger.warning(log % (panel_id, data)) # noqa: G002 + else: + raise @classmethod def panel(cls, request_id: str, panel_id: str) -> Any: diff --git a/docs/changes.rst b/docs/changes.rst index c526a37b4..9ad3f1c80 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -12,6 +12,8 @@ Pending * Defines the ``BaseStore`` interface for request storage mechanisms. * Added the setting ``TOOLBAR_STORE_CLASS`` to configure the request storage mechanism. Defaults to ``debug_toolbar.store.MemoryStore``. +* Added setting ``SERIALIZATION_WARNINGS`` to log a warning when a + ``TypeError`` occurs during a panel's serialization. 4.1.0 (2023-05-15) ------------------ diff --git a/docs/configuration.rst b/docs/configuration.rst index f2f6b7de9..fd27aad82 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -306,6 +306,14 @@ Panel options the nested functions. The threshold is calculated by the root calls' cumulative time divided by this ratio. +* ``SERIALIZATION_WARNINGS`` + + Default: ``True`` + + If set to ``True`` then panels will log a warning if a ``TypeError`` is + raised when attempting to serialize a panel's stats. The default will + eventually be set to ``False`` and removed entirely. + * ``SHOW_TEMPLATE_CONTEXT`` Default: ``True`` diff --git a/tests/test_store.py b/tests/test_store.py index c51afde1e..1c17aaf96 100644 --- a/tests/test_store.py +++ b/tests/test_store.py @@ -1,3 +1,5 @@ +import logging + from django.test import TestCase from django.test.utils import override_settings @@ -93,6 +95,21 @@ def test_save_panel(self): self.assertEqual(list(self.store.request_ids()), ["bar"]) self.assertEqual(self.store.panel("bar", "bar.panel"), {"a": 1}) + def test_save_panel_serialization_warning(self): + """The store should warn the user about a serialization error.""" + self.assertLogs() + + with self.assertLogs("debug_toolbar.store", level=logging.WARNING) as logs: + self.store.save_panel("bar", "bar.panel", {"value": {"foo"}}) + + self.assertEqual( + logs.output, + [ + "WARNING:debug_toolbar.store:Panel (bar.panel) failed to " + "serialized data {'value': {'foo'}} properly." + ], + ) + def test_panel(self): self.assertEqual(self.store.panel("missing", "missing"), {}) self.store.save_panel("bar", "bar.panel", {"a": 1})