Source code for minitrino.core.logging.formatter

"""Logging formatter for Minitrino logger."""

import logging
import os
import sys
import textwrap

from click import style

from minitrino.core.logging.common import DEFAULT_INDENT, get_terminal_width
from minitrino.core.logging.levels import LogLevel


[docs] class MinitrinoLogFormatter(logging.Formatter): """Formatter for Minitrino logs.""" COLORS = { "DEBUG": "magenta", "INFO": "cyan", "WARNING": "yellow", "ERROR": "red", "CRITICAL": "red", } PREFIXES = { "DEBUG": "[v] ", "INFO": "[i] ", "WARNING": "[w] ", "ERROR": "[e] ", "CRITICAL": "[e] ", } def __init__(self, always_verbose=False): """Initialize the formatter.""" super().__init__() self.always_verbose = always_verbose self.enable_color = sys.stdout.isatty()
[docs] def format(self, record: logging.LogRecord) -> str: """Format a log record for output. Parameters ---------- record : logging.LogRecord The log record to format. Returns ------- str The formatted log message. """ msg = record.getMessage() if not msg.strip(): return "" prefix = self._get_prefix(record) left = self._get_left_prefix(record, prefix) lines = msg.splitlines() if not lines: return left if sys.stdout.isatty(): return self._wrap_lines_tty(lines, left) else: return self._wrap_lines_plain(lines, left)
def _get_prefix(self, record: logging.LogRecord) -> str: """Get the prefix for the log record, applying color if enabled. Parameters ---------- record : logging.LogRecord The log record. Returns ------- str The prefix. """ prefix = self.PREFIXES.get(record.levelname, LogLevel.INFO.prefix) color = self.COLORS.get(record.levelname, LogLevel.INFO.color) if self.enable_color: return style(prefix, fg=color, bold=True) return prefix def _get_left_prefix(self, record: logging.LogRecord, prefix: str) -> str: """Get the left-side prefix for the log message. Parameters ---------- record : logging.LogRecord The log record. prefix : str The prefix string (styled or plain). Returns ------- str The left prefix for the message. """ fq_caller = getattr(record, "fq_caller", "") if self.always_verbose or record.levelno == logging.DEBUG: if fq_caller: return f"{prefix}{fq_caller} " elif record.pathname: return f"{prefix}{os.path.basename(record.pathname)}:{record.lineno} " return prefix def _wrap_lines_tty(self, lines: list[str], left: str) -> str: """Wrap lines for TTY output using textwrap, with indentation. Parameters ---------- lines : list of str The message lines to wrap. left : str The left prefix for the first line. Returns ------- str The wrapped message. """ # First line gets the prefix, subsequent lines get default indent first_wrapper = textwrap.TextWrapper( width=get_terminal_width(), initial_indent=left, subsequent_indent=DEFAULT_INDENT, ) # Subsequent original lines get default indent other_wrapper = textwrap.TextWrapper( width=get_terminal_width(), initial_indent=DEFAULT_INDENT, subsequent_indent=DEFAULT_INDENT, ) wrapped_lines = [] for i, line in enumerate(lines): if i == 0: wrapped_lines.append(first_wrapper.fill(line)) else: wrapped_lines.append(other_wrapper.fill(line)) return "\n".join(wrapped_lines) def _wrap_lines_plain(self, lines: list[str], left: str) -> str: """Format lines for non-TTY output. Parameters ---------- lines : list of str The message lines to wrap. left : str The left prefix for the first line. Returns ------- str The formatted message. """ return "\n".join( [f"{left}{lines[0]}"] + [f"{DEFAULT_INDENT}{line}" for line in lines[1:]] )