68 lines
2.4 KiB
Python
Raw Permalink Normal View History

2025-03-18 00:17:27 +05:30
from __future__ import annotations
import logging
import sys
from pathlib import Path
from typing import Any, IO, NoReturn, Optional, Union
import coloredlogs
LOG_FORMAT = "{asctime} [{levelname[0]}] {name} : {message}"
LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
LOG_STYLE = "{"
LOG_FORMATTER = logging.Formatter(LOG_FORMAT, LOG_DATE_FORMAT, LOG_STYLE)
class Logger(logging.Logger):
def __init__(self, name: str = "root", level: int = logging.NOTSET, color: bool = True):
"""Initialize the logger with a name and an optional level."""
super().__init__(name, level)
if self.name == "root":
self.add_stream_handler()
if color:
self.install_color()
def exit(self, msg: str, *args: Any, code: int = 1, **kwargs: Any) -> NoReturn:
"""
Log 'msg % args' with severity 'CRITICAL' and terminate the program
with a default exit code of 1.
To pass exception information, use the keyword argument exc_info with
a true value, e.g.
logger.exit("Houston, we have a %s", "major disaster", exc_info=1)
"""
self.critical(msg, *args, **kwargs)
sys.exit(code)
def add_stream_handler(self, stream: Optional[IO[str]] = None) -> None:
"""Add a stream handler to log. Stream defaults to stdout."""
sh = logging.StreamHandler(stream)
sh.setFormatter(LOG_FORMATTER)
self.addHandler(sh)
def add_file_handler(self, fp: Union[IO, Path, str]) -> None:
"""Convenience alias func for add_stream_handler, deals with type of fp object input."""
if not isinstance(fp, IO):
fp = Path(fp)
fp = fp.open("w", encoding="utf8")
self.add_stream_handler(fp)
def install_color(self) -> None:
"""Use coloredlogs to set up colors on the log output."""
if self.level == logging.DEBUG:
coloredlogs.install(level=self.level, fmt=LOG_FORMAT, datefmt=LOG_DATE_FORMAT, style=LOG_STYLE)
coloredlogs.install(level=self.level, logger=self, fmt=LOG_FORMAT, datefmt=LOG_DATE_FORMAT, style=LOG_STYLE)
# Cache already used loggers to make sure their level is preserved
_loggers: dict[str, Logger] = {}
# noinspection PyPep8Naming
def getLogger(name: Optional[str] = None, level: int = logging.NOTSET) -> Logger:
name = name or "root"
_log = _loggers.get(name, Logger(name))
_log.setLevel(level)
return _log