log9er.logger API documentation

# log9er.logger

View Source

      1import os
      2import logging
      3from logging.handlers import RotatingFileHandler, SysLogHandler
      4
      5
      6class Logger(object):
      7    """A basic logger supporting stderr, syslog, and rotating local file logging.
      8
      9    Attributes:
     10        debug (bool): If True, traceback is logged when `exception=True`.
     11        logger (logging.Logger | None): The underlying logger instance, if already provided
     12            or created in `__init__`.
     13    """
     14
     15    debug : bool = False
     16    logger : logging.Logger | None = None
     17
     18    # Default rotating file handler settings
     19    max_bytes : int = 10485760
     20    backup_count : int = 5
     21
     22    level_map : dict[str, int] = {
     23        "DEBUG": logging.DEBUG,
     24        "INFO": logging.INFO,
     25        "WARNING": logging.WARNING,
     26        "ERROR": logging.ERROR,
     27        "CRITICAL": logging.CRITICAL,
     28    }
     29
     30    def __init__(
     31            self,
     32            name : str = "",
     33            log : str = "syslog",
     34            level : int | str = logging.INFO,
     35            debug : bool = False,
     36            propagate : bool = False,
     37            max_bytes : int | None = None,
     38            backup_count : int | None = None,
     39            logger : logging.Logger | None = None
     40    ):
     41        """Initializes the Logger.
     42
     43        The `Logger` class provides a flexible interface for logging messages to various
     44        backends (such as syslog, file-based logging, or stderr) with customizable
     45        log levels. If an existing `logging.Logger` instance is provided, all other
     46        initialization parameters (except `debug`) are ignored.
     47
     48        Args:
     49            name (str): The name to assign to the logger instance. Required unless an
     50                external `logger` is provided.
     51            log (str): The log output destination. For example, `"syslog"` or a file path.
     52                If `"stderr"`, logs go to standard output.
     53            level (int | str): The default logging level. Can be a string
     54                (e.g., `"INFO"`) or an integer constant from the `logging` module
     55                (e.g., `logging.INFO`).
     56            debug (bool): If True, enable traceback logging when `exception=True`.
     57            propagate (bool): Whether log records should propagate to the root logger.
     58                If True, the root logger also processes these messages (default: False).
     59            max_bytes (int | None): If provided, and if `log` is a file path, use as the
     60                `maxBytes` for the `RotatingFileHandler` (default: 10485760).
     61            backup_count (int | None): If provided, and if `log` is a file path, use as
     62                the `backup_count` for the `RotatingFileHandler` (default: 5).
     63            logger (logging.Logger | None): If provided, use this logger instance directly
     64                and ignore other arguments (except `debug`).
     65
     66        Raises:
     67            TypeError: If `logger` is provided but is not an instance of `logging.Logger`.
     68            ValueError: If `level` is an invalid log level string.
     69            FileNotFoundError: If `log` is a file path and its directory does not exist.
     70            OSError: If `log="syslog"` but `/dev/log` is not available on this system.
     71        """
     72
     73        self.debug = debug
     74
     75        # Logger provided? Then ignore other arguments
     76        if logger is not None:
     77            if not isinstance(logger, logging.Logger):
     78                raise TypeError(
     79                    f"'logger' must be a 'logging.Logger' instance, not {type(logger)}"
     80                )
     81
     82            self.logger = logger
     83            return
     84
     85        # No logger provided, so 'name' is required
     86        if not name:
     87            raise ValueError("'name' is required unless an external 'logger' is provided")
     88
     89        # Otherwise create a new logger from args
     90        if isinstance(max_bytes, int):
     91            self.max_bytes = max_bytes
     92        if isinstance(backup_count, int):
     93            self.backup_count = backup_count
     94
     95        self._init_logger(name, log, level, propagate)
     96
     97    def _init_logger(
     98        self,
     99        name: str,
    100        log: str,
    101        level: int | str,
    102        propagate: bool
    103    ) -> None:
    104        """
    105        Internal helper to create and configure a new logger.
    106        Raises FileNotFoundError, OSError, or ValueError if invalid arguments.
    107        """
    108
    109        # If log is a file path, ensure the directory exists
    110        if log not in ["stderr", "syslog"]:
    111            dirpath = os.path.dirname(log)
    112            if not os.path.isdir(dirpath):
    113                raise FileNotFoundError(f"Log file directory not found: {dirpath}")
    114
    115        # Check syslog support
    116        if log == "syslog" and not os.path.exists("/dev/log"):
    117            raise OSError("This system does not support 'syslog' logging.")
    118
    119        # If level is a string, map it to a logging.* constant
    120        if isinstance(level, str):
    121            level = level.upper()
    122            if level not in self.level_map:
    123                raise ValueError(f"Invalid log level '{level}'")
    124            level = self.level_map[level]
    125
    126        # Logger already has handlers? Then let sleeping dogs lie
    127        logger = logging.getLogger(name)
    128        if logger.hasHandlers():
    129            self.logger = logger
    130            return
    131
    132        # OK safe to make the logger, now...
    133        logger.propagate = propagate
    134        logger.setLevel(level)
    135
    136        datefmt = "%b %d %H:%M:%S"
    137        msgfmt = "%(asctime)s %(name)s[%(process)s]: [%(levelname)s] %(message)s"
    138
    139        # Select a formatter
    140        if log == "stderr":
    141            formatter = ErrorColorFormatter(fmt=msgfmt, datefmt=datefmt)
    142        else:
    143            formatter = logging.Formatter(msgfmt, datefmt=datefmt)
    144
    145        # Select a handler
    146        if log == "stderr":
    147            handler = logging.StreamHandler()
    148        elif log == "syslog":
    149            handler = SysLogHandler(address="/dev/log")
    150        else:
    151            # Rotating file
    152            handler = RotatingFileHandler(
    153                log, maxBytes=self.max_bytes, backupCount=self.max_bytes
    154            )
    155
    156        handler.setFormatter(formatter)
    157        logger.addHandler(handler)
    158
    159        self.logger = logger
    160
    161    def log(
    162        self,
    163        *msgs : str,
    164        level : int | str = logging.INFO,
    165        exception : bool = False,
    166        prefix : str | None = None
    167    ) -> None:
    168        """Logs one or more messages at the specified level.
    169
    170        If multiple messages are passed, they are joined with a newline+tab ("\n\t").
    171        If `exception=True`, the messages are prefixed with "Exception:" and a traceback
    172        is logged if `debug=True`. If a `prefix` string is provided, it is inserted in
    173        square brackets before the message text.
    174
    175        Args:
    176            *msgs (str): One or more strings to log, e.g. `"Hello"`, `"World"`.
    177            level (int | str): The log level (e.g., `logging.INFO` or `"INFO"`). Defaults
    178                to `logging.INFO`.
    179            exception (bool): If True, prefix with "Exception:" and log a traceback if
    180                `self.debug` is also True.
    181            prefix (str | None): Optional prefix label. If provided, the log message is
    182                prefixed with e.g. `"[myfunction] message"`.
    183
    184        Returns:
    185            None
    186        """
    187
    188        # Convert and join all msgs
    189        msg = "\n\t".join([str(m) for m in msgs])
    190
    191        if exception:
    192            msg = f"Exception: {msg}"
    193
    194        if prefix:
    195            msg = f"[{prefix}] {msg}"
    196
    197        # If level is a string, map it to a logging.* constant
    198        if not isinstance(level, int) and not isinstance(level, str):
    199            raise ValueError(f"Invalid log level '{level}'. Must be `logging` level constant or string.")
    200
    201        if isinstance(level, str):
    202            level = level.upper()
    203            if level not in self.level_map:
    204                raise ValueError(f"Invalid log level '{level}'")
    205            level = self.level_map[level]
    206
    207        # If we're dealing with an exception and debug is enabled, log with traceback
    208        if exception and self.debug:
    209            # logger.exception() always logs at ERROR level + traceback
    210            self.logger.exception(msg)
    211            return
    212
    213        # Otherwise, if exception=True but debug=False, just log as ERROR without traceback
    214        if exception:
    215            level = logging.ERROR
    216
    217        # Log normally at the resolved level
    218        if level == logging.DEBUG:
    219            self.logger.debug(msg)
    220        elif level == logging.INFO:
    221            self.logger.info(msg)
    222        elif level == logging.WARNING:
    223            self.logger.warning(msg)
    224        elif level == logging.ERROR:
    225            self.logger.error(msg)
    226        elif level == logging.FATAL:
    227            self.logger.fatal(msg)
    228        else:
    229            self.logger.log(level, msg)
    230
    231
    232class ErrorColorFormatter(logging.Formatter):
    233    """A custom formatter that applies bold red color for ERROR or CRITICAL messages.
    234
    235    Attributes:
    236        BOLD_RED (str): The ANSI escape sequence for bold red text.
    237        RESET (str): The ANSI escape sequence to reset color.
    238    """
    239
    240    BOLD_RED = "\x1b[1;31m"
    241    RESET = "\x1b[0m"
    242
    243    def format(self, record: logging.LogRecord) -> str:
    244        """Formats a log record.
    245
    246        If the level is ERROR or CRITICAL, the message is colored in bold red.
    247
    248        Args:
    249            record (logging.LogRecord): The record to format.
    250
    251        Returns:
    252            str: The formatted log message, colored if needed.
    253        """
    254
    255        log_msg = super().format(record)
    256        if record.levelno >= logging.ERROR:
    257            return f"{self.BOLD_RED}{log_msg}{self.RESET}"
    258        return log_msg

class Logger: View Source

      7class Logger(object):
      8    """A basic logger supporting stderr, syslog, and rotating local file logging.
      9
     10    Attributes:
     11        debug (bool): If True, traceback is logged when `exception=True`.
     12        logger (logging.Logger | None): The underlying logger instance, if already provided
     13            or created in `__init__`.
     14    """
     15
     16    debug : bool = False
     17    logger : logging.Logger | None = None
     18
     19    # Default rotating file handler settings
     20    max_bytes : int = 10485760
     21    backup_count : int = 5
     22
     23    level_map : dict[str, int] = {
     24        "DEBUG": logging.DEBUG,
     25        "INFO": logging.INFO,
     26        "WARNING": logging.WARNING,
     27        "ERROR": logging.ERROR,
     28        "CRITICAL": logging.CRITICAL,
     29    }
     30
     31    def __init__(
     32            self,
     33            name : str = "",
     34            log : str = "syslog",
     35            level : int | str = logging.INFO,
     36            debug : bool = False,
     37            propagate : bool = False,
     38            max_bytes : int | None = None,
     39            backup_count : int | None = None,
     40            logger : logging.Logger | None = None
     41    ):
     42        """Initializes the Logger.
     43
     44        The `Logger` class provides a flexible interface for logging messages to various
     45        backends (such as syslog, file-based logging, or stderr) with customizable
     46        log levels. If an existing `logging.Logger` instance is provided, all other
     47        initialization parameters (except `debug`) are ignored.
     48
     49        Args:
     50            name (str): The name to assign to the logger instance. Required unless an
     51                external `logger` is provided.
     52            log (str): The log output destination. For example, `"syslog"` or a file path.
     53                If `"stderr"`, logs go to standard output.
     54            level (int | str): The default logging level. Can be a string
     55                (e.g., `"INFO"`) or an integer constant from the `logging` module
     56                (e.g., `logging.INFO`).
     57            debug (bool): If True, enable traceback logging when `exception=True`.
     58            propagate (bool): Whether log records should propagate to the root logger.
     59                If True, the root logger also processes these messages (default: False).
     60            max_bytes (int | None): If provided, and if `log` is a file path, use as the
     61                `maxBytes` for the `RotatingFileHandler` (default: 10485760).
     62            backup_count (int | None): If provided, and if `log` is a file path, use as
     63                the `backup_count` for the `RotatingFileHandler` (default: 5).
     64            logger (logging.Logger | None): If provided, use this logger instance directly
     65                and ignore other arguments (except `debug`).
     66
     67        Raises:
     68            TypeError: If `logger` is provided but is not an instance of `logging.Logger`.
     69            ValueError: If `level` is an invalid log level string.
     70            FileNotFoundError: If `log` is a file path and its directory does not exist.
     71            OSError: If `log="syslog"` but `/dev/log` is not available on this system.
     72        """
     73
     74        self.debug = debug
     75
     76        # Logger provided? Then ignore other arguments
     77        if logger is not None:
     78            if not isinstance(logger, logging.Logger):
     79                raise TypeError(
     80                    f"'logger' must be a 'logging.Logger' instance, not {type(logger)}"
     81                )
     82
     83            self.logger = logger
     84            return
     85
     86        # No logger provided, so 'name' is required
     87        if not name:
     88            raise ValueError("'name' is required unless an external 'logger' is provided")
     89
     90        # Otherwise create a new logger from args
     91        if isinstance(max_bytes, int):
     92            self.max_bytes = max_bytes
     93        if isinstance(backup_count, int):
     94            self.backup_count = backup_count
     95
     96        self._init_logger(name, log, level, propagate)
     97
     98    def _init_logger(
     99        self,
    100        name: str,
    101        log: str,
    102        level: int | str,
    103        propagate: bool
    104    ) -> None:
    105        """
    106        Internal helper to create and configure a new logger.
    107        Raises FileNotFoundError, OSError, or ValueError if invalid arguments.
    108        """
    109
    110        # If log is a file path, ensure the directory exists
    111        if log not in ["stderr", "syslog"]:
    112            dirpath = os.path.dirname(log)
    113            if not os.path.isdir(dirpath):
    114                raise FileNotFoundError(f"Log file directory not found: {dirpath}")
    115
    116        # Check syslog support
    117        if log == "syslog" and not os.path.exists("/dev/log"):
    118            raise OSError("This system does not support 'syslog' logging.")
    119
    120        # If level is a string, map it to a logging.* constant
    121        if isinstance(level, str):
    122            level = level.upper()
    123            if level not in self.level_map:
    124                raise ValueError(f"Invalid log level '{level}'")
    125            level = self.level_map[level]
    126
    127        # Logger already has handlers? Then let sleeping dogs lie
    128        logger = logging.getLogger(name)
    129        if logger.hasHandlers():
    130            self.logger = logger
    131            return
    132
    133        # OK safe to make the logger, now...
    134        logger.propagate = propagate
    135        logger.setLevel(level)
    136
    137        datefmt = "%b %d %H:%M:%S"
    138        msgfmt = "%(asctime)s %(name)s[%(process)s]: [%(levelname)s] %(message)s"
    139
    140        # Select a formatter
    141        if log == "stderr":
    142            formatter = ErrorColorFormatter(fmt=msgfmt, datefmt=datefmt)
    143        else:
    144            formatter = logging.Formatter(msgfmt, datefmt=datefmt)
    145
    146        # Select a handler
    147        if log == "stderr":
    148            handler = logging.StreamHandler()
    149        elif log == "syslog":
    150            handler = SysLogHandler(address="/dev/log")
    151        else:
    152            # Rotating file
    153            handler = RotatingFileHandler(
    154                log, maxBytes=self.max_bytes, backupCount=self.max_bytes
    155            )
    156
    157        handler.setFormatter(formatter)
    158        logger.addHandler(handler)
    159
    160        self.logger = logger
    161
    162    def log(
    163        self,
    164        *msgs : str,
    165        level : int | str = logging.INFO,
    166        exception : bool = False,
    167        prefix : str | None = None
    168    ) -> None:
    169        """Logs one or more messages at the specified level.
    170
    171        If multiple messages are passed, they are joined with a newline+tab ("\n\t").
    172        If `exception=True`, the messages are prefixed with "Exception:" and a traceback
    173        is logged if `debug=True`. If a `prefix` string is provided, it is inserted in
    174        square brackets before the message text.
    175
    176        Args:
    177            *msgs (str): One or more strings to log, e.g. `"Hello"`, `"World"`.
    178            level (int | str): The log level (e.g., `logging.INFO` or `"INFO"`). Defaults
    179                to `logging.INFO`.
    180            exception (bool): If True, prefix with "Exception:" and log a traceback if
    181                `self.debug` is also True.
    182            prefix (str | None): Optional prefix label. If provided, the log message is
    183                prefixed with e.g. `"[myfunction] message"`.
    184
    185        Returns:
    186            None
    187        """
    188
    189        # Convert and join all msgs
    190        msg = "\n\t".join([str(m) for m in msgs])
    191
    192        if exception:
    193            msg = f"Exception: {msg}"
    194
    195        if prefix:
    196            msg = f"[{prefix}] {msg}"
    197
    198        # If level is a string, map it to a logging.* constant
    199        if not isinstance(level, int) and not isinstance(level, str):
    200            raise ValueError(f"Invalid log level '{level}'. Must be `logging` level constant or string.")
    201
    202        if isinstance(level, str):
    203            level = level.upper()
    204            if level not in self.level_map:
    205                raise ValueError(f"Invalid log level '{level}'")
    206            level = self.level_map[level]
    207
    208        # If we're dealing with an exception and debug is enabled, log with traceback
    209        if exception and self.debug:
    210            # logger.exception() always logs at ERROR level + traceback
    211            self.logger.exception(msg)
    212            return
    213
    214        # Otherwise, if exception=True but debug=False, just log as ERROR without traceback
    215        if exception:
    216            level = logging.ERROR
    217
    218        # Log normally at the resolved level
    219        if level == logging.DEBUG:
    220            self.logger.debug(msg)
    221        elif level == logging.INFO:
    222            self.logger.info(msg)
    223        elif level == logging.WARNING:
    224            self.logger.warning(msg)
    225        elif level == logging.ERROR:
    226            self.logger.error(msg)
    227        elif level == logging.FATAL:
    228            self.logger.fatal(msg)
    229        else:
    230            self.logger.log(level, msg)

A basic logger supporting stderr, syslog, and rotating local file logging.

###### Attributes:

- debug (bool): If True, traceback is logged when `exception=True`.
- logger (logging.Logger | None): The underlying logger instance, if already provided or created in `__init__`.

Logger( name: str = '', log: str = 'syslog', level: int | str = 20, debug: bool = False, propagate: bool = False, max_bytes: int | None = None, backup_count: int | None = None, logger: logging.Logger | None = None) View Source

    31    def __init__(
    32            self,
    33            name : str = "",
    34            log : str = "syslog",
    35            level : int | str = logging.INFO,
    36            debug : bool = False,
    37            propagate : bool = False,
    38            max_bytes : int | None = None,
    39            backup_count : int | None = None,
    40            logger : logging.Logger | None = None
    41    ):
    42        """Initializes the Logger.
    43
    44        The `Logger` class provides a flexible interface for logging messages to various
    45        backends (such as syslog, file-based logging, or stderr) with customizable
    46        log levels. If an existing `logging.Logger` instance is provided, all other
    47        initialization parameters (except `debug`) are ignored.
    48
    49        Args:
    50            name (str): The name to assign to the logger instance. Required unless an
    51                external `logger` is provided.
    52            log (str): The log output destination. For example, `"syslog"` or a file path.
    53                If `"stderr"`, logs go to standard output.
    54            level (int | str): The default logging level. Can be a string
    55                (e.g., `"INFO"`) or an integer constant from the `logging` module
    56                (e.g., `logging.INFO`).
    57            debug (bool): If True, enable traceback logging when `exception=True`.
    58            propagate (bool): Whether log records should propagate to the root logger.
    59                If True, the root logger also processes these messages (default: False).
    60            max_bytes (int | None): If provided, and if `log` is a file path, use as the
    61                `maxBytes` for the `RotatingFileHandler` (default: 10485760).
    62            backup_count (int | None): If provided, and if `log` is a file path, use as
    63                the `backup_count` for the `RotatingFileHandler` (default: 5).
    64            logger (logging.Logger | None): If provided, use this logger instance directly
    65                and ignore other arguments (except `debug`).
    66
    67        Raises:
    68            TypeError: If `logger` is provided but is not an instance of `logging.Logger`.
    69            ValueError: If `level` is an invalid log level string.
    70            FileNotFoundError: If `log` is a file path and its directory does not exist.
    71            OSError: If `log="syslog"` but `/dev/log` is not available on this system.
    72        """
    73
    74        self.debug = debug
    75
    76        # Logger provided? Then ignore other arguments
    77        if logger is not None:
    78            if not isinstance(logger, logging.Logger):
    79                raise TypeError(
    80                    f"'logger' must be a 'logging.Logger' instance, not {type(logger)}"
    81                )
    82
    83            self.logger = logger
    84            return
    85
    86        # No logger provided, so 'name' is required
    87        if not name:
    88            raise ValueError("'name' is required unless an external 'logger' is provided")
    89
    90        # Otherwise create a new logger from args
    91        if isinstance(max_bytes, int):
    92            self.max_bytes = max_bytes
    93        if isinstance(backup_count, int):
    94            self.backup_count = backup_count
    95
    96        self._init_logger(name, log, level, propagate)

Initializes the Logger.

The `Logger` class provides a flexible interface for logging messages to various backends (such as syslog, file-based logging, or stderr) with customizable log levels. If an existing `logging.Logger` instance is provided, all other initialization parameters (except `debug`) are ignored.

###### Arguments:

- name (str): The name to assign to the logger instance. Required unless an external `logger` is provided.
- log (str): The log output destination. For example, `"syslog"` or a file path. If `"stderr"`, logs go to standard output.
- level (int | str): The default logging level. Can be a string (e.g., `"INFO"`) or an integer constant from the `logging` module (e.g., `logging.INFO`).
- debug (bool): If True, enable traceback logging when `exception=True`.
- propagate (bool): Whether log records should propagate to the root logger. If True, the root logger also processes these messages (default: False).
- max_bytes (int | None): If provided, and if `log` is a file path, use as the `maxBytes` for the `RotatingFileHandler` (default: 10485760).
- backup_count (int | None): If provided, and if `log` is a file path, use as the `backup_count` for the `RotatingFileHandler` (default: 5).
- logger (logging.Logger | None): If provided, use this logger instance directly and ignore other arguments (except `debug`).

###### Raises:

- TypeError: If `logger` is provided but is not an instance of `logging.Logger`.
- ValueError: If `level` is an invalid log level string.
- FileNotFoundError: If `log` is a file path and its directory does not exist.
- OSError: If `log="syslog"` but `/dev/log` is not available on this system.

debug: bool = False
logger: logging.Logger | None = None
max_bytes: int = 10485760
backup_count: int = 5
level_map: dict[str, int] = {'DEBUG': 10, 'INFO': 20, 'WARNING': 30, 'ERROR': 40, 'CRITICAL': 50}
def log( self, *msgs: str, level: int | str = 20, exception: bool = False, prefix: str | None = None) -> None: View Source

    162    def log(
    163        self,
    164        *msgs : str,
    165        level : int | str = logging.INFO,
    166        exception : bool = False,
    167        prefix : str | None = None
    168    ) -> None:
    169        """Logs one or more messages at the specified level.
    170
    171        If multiple messages are passed, they are joined with a newline+tab ("\n\t").
    172        If `exception=True`, the messages are prefixed with "Exception:" and a traceback
    173        is logged if `debug=True`. If a `prefix` string is provided, it is inserted in
    174        square brackets before the message text.
    175
    176        Args:
    177            *msgs (str): One or more strings to log, e.g. `"Hello"`, `"World"`.
    178            level (int | str): The log level (e.g., `logging.INFO` or `"INFO"`). Defaults
    179                to `logging.INFO`.
    180            exception (bool): If True, prefix with "Exception:" and log a traceback if
    181                `self.debug` is also True.
    182            prefix (str | None): Optional prefix label. If provided, the log message is
    183                prefixed with e.g. `"[myfunction] message"`.
    184
    185        Returns:
    186            None
    187        """
    188
    189        # Convert and join all msgs
    190        msg = "\n\t".join([str(m) for m in msgs])
    191
    192        if exception:
    193            msg = f"Exception: {msg}"
    194
    195        if prefix:
    196            msg = f"[{prefix}] {msg}"
    197
    198        # If level is a string, map it to a logging.* constant
    199        if not isinstance(level, int) and not isinstance(level, str):
    200            raise ValueError(f"Invalid log level '{level}'. Must be `logging` level constant or string.")
    201
    202        if isinstance(level, str):
    203            level = level.upper()
    204            if level not in self.level_map:
    205                raise ValueError(f"Invalid log level '{level}'")
    206            level = self.level_map[level]
    207
    208        # If we're dealing with an exception and debug is enabled, log with traceback
    209        if exception and self.debug:
    210            # logger.exception() always logs at ERROR level + traceback
    211            self.logger.exception(msg)
    212            return
    213
    214        # Otherwise, if exception=True but debug=False, just log as ERROR without traceback
    215        if exception:
    216            level = logging.ERROR
    217
    218        # Log normally at the resolved level
    219        if level == logging.DEBUG:
    220            self.logger.debug(msg)
    221        elif level == logging.INFO:
    222            self.logger.info(msg)
    223        elif level == logging.WARNING:
    224            self.logger.warning(msg)
    225        elif level == logging.ERROR:
    226            self.logger.error(msg)
    227        elif level == logging.FATAL:
    228            self.logger.fatal(msg)
    229        else:
    230            self.logger.log(level, msg)

Logs one or more messages at the specified level.

If multiple messages are passed, they are joined with a newline+tab (" "). If `exception=True`, the messages are prefixed with "Exception:" and a traceback is logged if `debug=True`. If a `prefix` string is provided, it is inserted in square brackets before the message text.

###### Arguments:

- *msgs (str): One or more strings to log, e.g. `"Hello"`, `"World"`.
- level (int | str): The log level (e.g., `logging.INFO` or `"INFO"`). Defaults to `logging.INFO`.
- exception (bool): If True, prefix with "Exception:" and log a traceback if `self.debug` is also True.
- prefix (str | None): Optional prefix label. If provided, the log message is prefixed with e.g. `"[myfunction] message"`.

###### Returns:

> None

class ErrorColorFormatter(logging.Formatter): View Source

    233class ErrorColorFormatter(logging.Formatter):
    234    """A custom formatter that applies bold red color for ERROR or CRITICAL messages.
    235
    236    Attributes:
    237        BOLD_RED (str): The ANSI escape sequence for bold red text.
    238        RESET (str): The ANSI escape sequence to reset color.
    239    """
    240
    241    BOLD_RED = "\x1b[1;31m"
    242    RESET = "\x1b[0m"
    243
    244    def format(self, record: logging.LogRecord) -> str:
    245        """Formats a log record.
    246
    247        If the level is ERROR or CRITICAL, the message is colored in bold red.
    248
    249        Args:
    250            record (logging.LogRecord): The record to format.
    251
    252        Returns:
    253            str: The formatted log message, colored if needed.
    254        """
    255
    256        log_msg = super().format(record)
    257        if record.levelno >= logging.ERROR:
    258            return f"{self.BOLD_RED}{log_msg}{self.RESET}"
    259        return log_msg

A custom formatter that applies bold red color for ERROR or CRITICAL messages.

###### Attributes:

- BOLD_RED (str): The ANSI escape sequence for bold red text.
- RESET (str): The ANSI escape sequence to reset color.

BOLD_RED = '\x1b[1;31m'
RESET = '\x1b[0m'
def format(self, record: logging.LogRecord) -> str: View Source

    244    def format(self, record: logging.LogRecord) -> str:
    245        """Formats a log record.
    246
    247        If the level is ERROR or CRITICAL, the message is colored in bold red.
    248
    249        Args:
    250            record (logging.LogRecord): The record to format.
    251
    252        Returns:
    253            str: The formatted log message, colored if needed.
    254        """
    255
    256        log_msg = super().format(record)
    257        if record.levelno >= logging.ERROR:
    258            return f"{self.BOLD_RED}{log_msg}{self.RESET}"
    259        return log_msg

Formats a log record.

If the level is ERROR or CRITICAL, the message is colored in bold red.

###### Arguments:

- record (logging.LogRecord): The record to format.

###### Returns:

> str: The formatted log message, colored if needed.