Source code for debug_toolbar.core.panels.logging

"""Logging panel for capturing log records during requests."""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any, ClassVar

from debug_toolbar.core.panel import Panel

if TYPE_CHECKING:
    from debug_toolbar.core.context import RequestContext
    from debug_toolbar.core.toolbar import DebugToolbar


class ToolbarLoggingHandler(logging.Handler):
    """Logging handler that captures records for the debug toolbar."""

    def __init__(self) -> None:
        super().__init__()
        self.records: list[dict[str, Any]] = []

    def emit(self, record: logging.LogRecord) -> None:
        """Capture a log record."""
        self.records.append(
            {
                "level": record.levelname,
                "level_no": record.levelno,
                "message": self.format(record),
                "name": record.name,
                "pathname": record.pathname,
                "lineno": record.lineno,
                "funcname": record.funcName,
                "created": record.created,
                "exc_info": record.exc_info is not None,
            }
        )

    def clear(self) -> None:
        """Clear captured records."""
        self.records = []


[docs] class LoggingPanel(Panel): """Panel displaying log records captured during the request. Shows: - Log level - Logger name - Message - Source location - Exception info (if present) """ panel_id: ClassVar[str] = "LoggingPanel" title: ClassVar[str] = "Logging" template: ClassVar[str] = "panels/logging.html" has_content: ClassVar[bool] = True nav_title: ClassVar[str] = "Logs" __slots__ = ("_handler",)
[docs] def __init__(self, toolbar: DebugToolbar) -> None: super().__init__(toolbar) self._handler = ToolbarLoggingHandler() self._handler.setFormatter(logging.Formatter("%(message)s"))
[docs] async def process_request(self, context: RequestContext) -> None: """Start capturing logs.""" self._handler.clear() logging.root.addHandler(self._handler)
[docs] async def process_response(self, context: RequestContext) -> None: """Stop capturing logs.""" logging.root.removeHandler(self._handler)
[docs] async def generate_stats(self, context: RequestContext) -> dict[str, Any]: """Generate logging statistics.""" records = list(self._handler.records) level_counts: dict[str, int] = {} for record in records: level = record["level"] level_counts[level] = level_counts.get(level, 0) + 1 return { "records": records, "count": len(records), "level_counts": level_counts, "has_errors": level_counts.get("ERROR", 0) > 0 or level_counts.get("CRITICAL", 0) > 0, "has_warnings": level_counts.get("WARNING", 0) > 0, }
[docs] def get_nav_subtitle(self) -> str: """Get the navigation subtitle showing log count.""" return ""