Skip to content

Commit

Permalink
Log serialization warning when a panel errors. (#1810)
Browse files Browse the repository at this point in the history
* Log serialization warning when a panel errors.

This will help third party panels identify issues with serializing
the content of their panels in the future, without causing the
entire toolbar to break.

* Change setting name to SUPPRESS_SERIALIZATION_ERRORS
  • Loading branch information
tim-schilling committed Aug 21, 2023
1 parent 97fcda7 commit 487dfb3
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 1 deletion.
1 change: 1 addition & 0 deletions debug_toolbar/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"PROFILER_CAPTURE_PROJECT_CODE": True,
"PROFILER_MAX_DEPTH": 10,
"PROFILER_THRESHOLD_RATIO": 8,
"SUPPRESS_SERIALIZATION_ERRORS": True,
"SHOW_TEMPLATE_CONTEXT": True,
"SKIP_TEMPLATE_PREFIXES": ("django/forms/widgets/", "admin/widgets/"),
"SQL_WARNING_THRESHOLD": 500, # milliseconds
Expand Down
12 changes: 11 additions & 1 deletion debug_toolbar/store.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import contextlib
import json
import logging
from collections import defaultdict, deque
from typing import Any, Dict, Iterable

Expand All @@ -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
Expand Down Expand Up @@ -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()["SUPPRESS_SERIALIZATION_ERRORS"]:
log = "Panel (%s) failed to serialized data %s properly."
logger.warning(log % (panel_id, data))
else:
raise

@classmethod
def panel(cls, request_id: str, panel_id: str) -> Any:
Expand Down
2 changes: 2 additions & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Serializable (don't include in main)
* 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 ``SUPPRESS_SERIALIZATION_ERRORS`` to suppress
warnings when a ``TypeError`` occurs during a panel's serialization.


Pending
Expand Down
9 changes: 9 additions & 0 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,15 @@ Panel options
the nested functions. The threshold is calculated by the root calls'
cumulative time divided by this ratio.

* ``SUPPRESS_SERIALIZATION_ERRORS``

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 rather than raising an
exception.. If set to ``False`` then the ``TypeError`` will be raised. The
default will eventually be set to ``False`` and removed entirely.

* ``SHOW_TEMPLATE_CONTEXT``

Default: ``True``
Expand Down
17 changes: 17 additions & 0 deletions tests/test_store.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import logging

from django.test import TestCase
from django.test.utils import override_settings

Expand Down Expand Up @@ -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})
Expand Down

0 comments on commit 487dfb3

Please sign in to comment.