Skip to content

Logging

logger

LogLevelColors

Bases: str, Enum

Colors for the log levels.

TextFormatter

TextFormatter(
    fmt: str = "[%(asctime)s] | %(pathname)s:%(lineno)d | %(levelname)s | %(message)s",
    datefmt: str = "%Y-%m-%d %H:%M:%S",
    use_colors: bool = True,
)

Bases: Formatter

Human-readable inline log formatter.

Source code in pokelance/logger.py
def __init__(
    self,
    fmt: str = "[%(asctime)s] | %(pathname)s:%(lineno)d | %(levelname)s | %(message)s",
    datefmt: str = "%Y-%m-%d %H:%M:%S",
    use_colors: bool = True,
) -> None:
    super().__init__(fmt=fmt, datefmt=datefmt)
    self.use_colors = use_colors

JSONFormatter

JSONFormatter(
    *,
    datefmt: str = "%Y-%m-%d %H:%M:%S",
    use_colors: bool = True,
)

Bases: Formatter

Structured JSON log formatter.

Source code in pokelance/logger.py
def __init__(
    self,
    *,
    datefmt: str = "%Y-%m-%d %H:%M:%S",
    use_colors: bool = True,
) -> None:
    super().__init__("%(levelname)s %(name)s %(message)s", datefmt=datefmt)
    self.use_colors = use_colors

DailyRotatingFileHandler

DailyRotatingFileHandler(
    filename: str | PathLike[str],
    mode: str = "a",
    max_bytes: int = 10 * 1024 * 1024,
    backup_count: int = 5,
    encoding: str | None = "utf-8",
    delay: bool = False,
    errors: str | None = None,
    structured: bool = True,
    *,
    folder: Path | str = "logs",
)

Bases: RotatingFileHandler

A file handler that writes log messages to daily rotating files.

Source code in pokelance/logger.py
def __init__(
    self,
    filename: str | os.PathLike[str],
    mode: str = "a",
    max_bytes: int = 10 * 1024 * 1024,
    backup_count: int = 5,
    encoding: str | None = "utf-8",
    delay: bool = False,
    errors: str | None = None,
    structured: bool = True,
    *,
    folder: pathlib.Path | str = "logs",
) -> None:
    self._last_entry = datetime.datetime.now(datetime.timezone.utc)
    self.folder = pathlib.Path(folder)
    self.filename = filename
    self.folder.mkdir(exist_ok=True)
    super().__init__(
        self.folder / f"{datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d')}-{self.filename}.log",
        mode=mode,
        maxBytes=max_bytes,
        backupCount=backup_count,
        encoding=encoding,
        delay=delay,
        errors=errors,
    )
    self.setFormatter(JSONFormatter(use_colors=False) if structured else TextFormatter(use_colors=False))
    self.addFilter(RelativePathFilter())

emit

emit(record: LogRecord) -> None

Emit a log record.

Source code in pokelance/logger.py
@override
def emit(self, record: logging.LogRecord) -> None:
    """Emit a log record."""
    now = datetime.datetime.now(datetime.timezone.utc)
    if self._last_entry.date() != now.date():
        self._last_entry = now
        self.close()
        self.baseFilename = (
            self.folder / f"{self._last_entry.strftime('%Y-%m-%d')}-{self.filename}.log"
        ).as_posix()
        self.stream = self._open()
    super().emit(record)

handle_exception

handle_exception(
    exc_type: type[BaseException],
    exc_value: BaseException,
    exc_traceback: TracebackType | None,
) -> None

Global unhandled exception hook handler.

Source code in pokelance/logger.py
def handle_exception(
    exc_type: type[BaseException], exc_value: BaseException, exc_traceback: TracebackType | None
) -> None:
    """Global unhandled exception hook handler."""
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return
    logger.critical("Unhandled exception", exc_info=(exc_type, exc_value, exc_traceback))

setup_logging

setup_logging(
    log_level: int = INFO,
    structured: bool = False,
    file_logging: bool = False,
    filename: str = "pokelance",
    log_dir: str | Path = "logs",
    set_excepthook: bool = True,
) -> None

Set up logging for console and optional file handlers on pokelance namespace.

Source code in pokelance/logger.py
def setup_logging(
    log_level: int = logging.INFO,
    structured: bool = False,
    file_logging: bool = False,
    filename: str = "pokelance",
    log_dir: str | pathlib.Path = "logs",
    set_excepthook: bool = True,
) -> None:
    """Set up logging for console and optional file handlers on pokelance namespace."""
    console_formatter = "json_colored" if structured else "text_colored"
    file_formatter = "json_plain" if structured else "text_plain"

    handlers: dict[str, dict[str, t.Any]] = {
        "console": {
            "class": "logging.StreamHandler",
            "level": log_level,
            "formatter": console_formatter,
            "filters": ["relative_path"],
            "stream": "ext://sys.stderr",
        },
    }

    if file_logging:
        handlers["file"] = {
            "()": DailyRotatingFileHandler,
            "level": log_level,
            "formatter": file_formatter,
            "filename": filename,
            "folder": str(log_dir),
            "structured": structured,
        }

    logging_config: dict[str, t.Any] = {
        "version": 1,
        "disable_existing_loggers": False,
        "filters": {
            "relative_path": {
                "()": RelativePathFilter,
            },
        },
        "formatters": {
            "text_colored": {
                "()": TextFormatter,
                "use_colors": True,
            },
            "text_plain": {
                "()": TextFormatter,
                "use_colors": False,
            },
            "json_colored": {
                "()": JSONFormatter,
                "use_colors": True,
            },
            "json_plain": {
                "()": JSONFormatter,
                "use_colors": False,
            },
        },
        "handlers": handlers,
        "loggers": {
            "pokelance": {
                "handlers": list(handlers.keys()),
                "level": log_level,
                "propagate": False,
            },
        },
    }

    logging.config.dictConfig(logging_config)

    if set_excepthook:
        sys.excepthook = handle_exception

Comments