Source code for bonafide.utils.logging_format

"""Formatting of logging messages for consistent indentation and line length."""

import logging
import textwrap
from typing import Literal, Optional


[docs] class IndentationFormatter(logging.Formatter): """Logging formatter that indents continuation lines to align with the start of the message. Parameters ---------- fmt : Optional[str], optional The format string for the log message, by default ``None``. datefmt : Optional[str], optional The format string for the date/time, by default ``None``. style : str, optional The style of the format string, by default ``"%"``. max_line_length : int, optional The maximum line length for the formatted message, by default ``150``. """ def __init__( self, fmt: Optional[str] = None, datefmt: Optional[str] = None, style: Literal["%", "{", "$"] = "%", max_line_length: int = 150, ) -> None: super().__init__(fmt=fmt, datefmt=datefmt, style=style) self.max_line_length = max_line_length
[docs] def format(self, record: logging.LogRecord) -> str: """Format logging records. Each logical line (between pre-existing line breaks) is wrapped individually. All continuation lines are indented to align with the start of the message. Parameters ---------- record : logging.LogRecord The logging record to format. Returns ------- str The formatted logging message with indented continuation lines. """ original_message = str(record.msg) # Get indentation record.msg = "" prefix = super().format(record) indentation = " " * len(prefix) # Split original message by existing newlines logical_lines = original_message.split("\n") wrapped_message_lines = [] for idx, logical_line in enumerate(logical_lines): # Wrap lines and introduce new line breaks if necessary wrapped = textwrap.wrap( logical_line, width=self.max_line_length - len(prefix), break_long_words=False, break_on_hyphens=False, ) if wrapped: # Apply prefix only to the first visual/logical line if idx == 0: wrapped_message_lines.append(prefix + wrapped[0]) for extra_line in wrapped[1:]: wrapped_message_lines.append(extra_line) else: for w in wrapped: wrapped_message_lines.append(w) # Empty lines else: wrapped_message_lines.append("") # Indent all subsequent lines (created by wrap and explicit newlines) for idx in range(1, len(wrapped_message_lines)): if wrapped_message_lines[idx]: # don't indent empty lines wrapped_message_lines[idx] = indentation + wrapped_message_lines[idx] return "\n".join(wrapped_message_lines)