Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion debug_toolbar/panels/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django.utils.functional import classproperty

from debug_toolbar import settings as dt_settings
from debug_toolbar.utils import get_name_from_obj
from debug_toolbar.utils import HealthLevel, get_name_from_obj


class Panel:
Expand Down Expand Up @@ -129,6 +129,19 @@ def scripts(self):
"""
return []

@property
def health_level(self):
"""
Returns the health level of the panel as a `ToolbarHealthLevel` enum value.

This property is used by the toolbar to determine the overall health status of each panel.
The default implementation returns `ToolbarHealthLevel.NONE`, indicating no issues.

Subclasses should override this property to provide custom health logic, returning
`ToolbarHealthLevel.WARNING` or `ToolbarHealthLevel.ERROR` as appropriate based on panel-specific conditions.
"""
return HealthLevel.NONE

# Panel early initialization

@classmethod
Expand Down
12 changes: 11 additions & 1 deletion debug_toolbar/panels/alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django.utils.translation import gettext_lazy as _

from debug_toolbar.panels import Panel
from debug_toolbar.utils import is_processable_html_response
from debug_toolbar.utils import HealthLevel, is_processable_html_response


class FormParser(HTMLParser):
Expand Down Expand Up @@ -92,6 +92,16 @@ def nav_subtitle(self):
else:
return ""

@property
def health_level(self):
"""
Return the health level of the panel based on the alerts.
"""
if not self.get_stats().get("alerts"):
return HealthLevel.NONE

return HealthLevel.CRITICAL

def add_alert(self, alert):
self.alerts.append(alert)

Expand Down
17 changes: 16 additions & 1 deletion debug_toolbar/panels/sql/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
is_select_query,
reformat_sql,
)
from debug_toolbar.utils import render_stacktrace
from debug_toolbar.utils import HealthLevel, render_stacktrace


def get_isolation_level_display(vendor, level):
Expand Down Expand Up @@ -186,6 +186,21 @@ def title(self):
count,
) % {"count": count}

@property
def health_level(self):
"""
Return the health level of the SQL panel.
This is determined by the number of slow queries recorded.
"""
stats = self.get_stats()
slow_queries = len([1 for q in stats.get("queries", []) if q.get("is_slow")])
if slow_queries > 10:
return HealthLevel.CRITICAL
elif slow_queries > 0:
return HealthLevel.WARNING

return super().health_level

template = "debug_toolbar/panels/sql.html"

@classmethod
Expand Down
37 changes: 37 additions & 0 deletions debug_toolbar/static/debug_toolbar/css/toolbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@
--djdt-button-border-color: var(--djdt-table-border-color);
--djdt-pre-border-color: var(--djdt-table-border-color);
--djdt-raw-border-color: var(--djdt-table-border-color);

--djdt-health-background-color-1: #4b3f1b;
--djdt-health-color-1: #ffe761;
--djdt-health-border-color-1: #ffcc00;
--djdt-health-background-color-2: #5a2327;
--djdt-health-color-2: #ffb3b3;
--djdt-health-border-color-2: #ff0000;
Comment on lines +38 to +43
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use a descriptive word rather than 1 and 2 here please?

}

#djDebug[data-theme="dark"] {
Expand All @@ -56,6 +63,13 @@
--djdt-button-border-color: var(--djdt-table-border-color);
--djdt-pre-border-color: var(--djdt-table-border-color);
--djdt-raw-border-color: var(--djdt-table-border-color);

--djdt-health-background-color-1: #4b3f1b;
--djdt-health-color-1: #ffe761;
--djdt-health-border-color-1: #ffcc00;
--djdt-health-background-color-2: #5a2327;
--djdt-health-color-2: #ffb3b3;
--djdt-health-border-color-2: #ff0000;
}

/* Debug Toolbar CSS Reset, adapted from Eric Meyer's CSS Reset */
Expand Down Expand Up @@ -377,6 +391,29 @@
animation: spin 2s linear infinite;
}

/* Panel and Toolbar hidden button health states */
#djDebug .djdt-health-1 {
/* The background can be shadowed by #djDebugToolbar li.djdt-active a:hover */
background: var(--djdt-health-background-color-1) !important;
color: var(--djdt-health-color-1);
}

#djDebug .djdt-health-2 {
/* The background can be shadowed by #djDebugToolbar li.djdt-active a:hover */
background: var(--djdt-health-background-color-2) !important;
color: var(--djdt-health-color-2);
}

#djDebug .djdt-toolbarhandle-health-1 {
/* The border-color is shadowed by the default #djShowToolBarButton border */
border-color: var(--djdt-health-border-color-1) !important;
}

#djDebug .djdt-toolbarhandle-health-2 {
/* The border-color is shadowed by the default #djShowToolBarButton border */
border-color: var(--djdt-health-border-color-2) !important;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment why all these !important are necessary? And/or remove them when they are not?


@keyframes spin {
0% {
transform: rotate(0deg);
Expand Down
2 changes: 1 addition & 1 deletion debug_toolbar/templates/debug_toolbar/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
</ul>
</div>
<div class="djdt-hidden" id="djDebugToolbarHandle">
<div title="{% translate 'Show toolbar' %}" id="djShowToolBarButton">
<div title="{% translate 'Show toolbar' %}" id="djShowToolBarButton" class="{% if toolbar.health_level %} djdt-toolbarhandle-health-{{ toolbar.health_level }}{% endif %}">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generally do not like uppercase letters much in ID and class attributes, but I wonder if we should keep capitalization for internal consistency here? Any thoughts?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are inconsistent right now. Some places use camelCase when starting class names with dj, while others use lowercase with dashes (mostly when starting with djdt).

Rightnow, I can change them to camelcase if you want, but for consistency the changes in whole project will be required in future.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, don't worry about it then. Being as consistent as possible is good enough.

<span id="djShowToolBarD">D</span><span id="djShowToolBarJ">J</span>DT
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{% load i18n %}

<li id="djdt-{{ panel.panel_id }}" class="djDebugPanelButton">
<li id="djdt-{{ panel.panel_id }}" class="djDebugPanelButton{% if panel.health_level %} djdt-health-{{ panel.health_level }}{% endif %}">
<input type="checkbox" data-cookie="djdt{{ panel.panel_id }}" {% if panel.enabled %}checked title="{% translate "Disable for next and successive requests" %}"{% else %}title="{% translate "Enable for next and successive requests" %}"{% endif %}>
{% if panel.has_content and panel.enabled %}
<a href="#" title="{{ panel.title }}" class="{{ panel.panel_id }}">
Expand Down
12 changes: 12 additions & 0 deletions debug_toolbar/toolbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

from debug_toolbar import APP_NAME, settings as dt_settings
from debug_toolbar.store import get_store
from debug_toolbar.utils import HealthLevel

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -72,6 +73,17 @@ def csp_nonce(self):
"""
return getattr(self.request, "csp_nonce", None)

@property
def health_level(self):
"""
Return the maximum health level across all panels.
This is used to color the toolbar hidden button.
"""
if not self.panels:
return HealthLevel.NONE

return max(panel.health_level for panel in self.enabled_panels)

def get_panel_by_id(self, panel_id):
"""
Get the panel with the given id, which is the class name by default.
Expand Down
16 changes: 16 additions & 0 deletions debug_toolbar/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import sys
import warnings
from collections.abc import Sequence
from enum import IntEnum
from pprint import PrettyPrinter, pformat
from typing import Any

Expand Down Expand Up @@ -401,3 +402,18 @@ def is_processable_html_response(response):
and content_encoding == ""
and content_type in _HTML_TYPES
)


class HealthLevel(IntEnum):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matthiask @hunzlahmalik do we want to bikeshed a bit on the name here? If we build out an API, this will likely be included in that and no longer be a private-ish thing. In my mind, "severity" may be more appropriate, but then it may lack the context of what is the severity in relation to.

Not sure, but want to bring it up since it's likely to become a public interface in the near future (< 1 years).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes we can change the name. I thought about it and was inclined to using the alert, but it was already taking in the app for the panel. We can use severity, we can see this as independent from the colorization. Later this can be use to get the panel severitylevel to do something else.

"""
Represents the health or alert level for a panel or the toolbar as a whole.

Used to indicate the severity of issues detected by panels, allowing the UI to reflect
warning or critical states (e.g., via colorization). Panels should return one of these
levels from their `health_level` property. The toolbar will aggregate the maximum level
across all panels to determine the overall toolbar health state.
"""

NONE = 0
WARNING = 1
CRITICAL = 2