"""ANSI color helpers and shared logger.""" from __future__ import annotations import logging import re as _re import sys _USE_COLOR = sys.stderr.isatty() def _c(code: str, text: str) -> str: return f"\033[{code}m{text}\033[0m" if _USE_COLOR else text def _dim(t: str) -> str: return _c("2", t) def _bold(t: str) -> str: return _c("1", t) def _red(t: str) -> str: return _c("31", t) def _green(t: str) -> str: return _c("32", t) def _yellow(t: str) -> str: return _c("33", t) def _cyan(t: str) -> str: return _c("36", t) def _bold_red(t: str) -> str: return _c("1;31", t) def _bold_green(t: str) -> str: return _c("1;32", t) def _bold_yellow(t: str) -> str: return _c("1;33", t) def _bold_cyan(t: str) -> str: return _c("1;36", t) def _vis_len(s: str) -> int: """Visible character width of a string, ignoring ANSI escape sequences.""" return len(_re.sub(r'\033\[[0-9;]*m', '', s)) class _ColorFormatter(logging.Formatter): _STYLES = { logging.DEBUG: "2", logging.INFO: "36", logging.WARNING: "1;33", logging.ERROR: "1;31", logging.CRITICAL: "1;31", } def format(self, record: logging.LogRecord) -> str: ts = self.formatTime(record, self.datefmt) msg = record.getMessage() if _USE_COLOR: code = self._STYLES.get(record.levelno, "0") level = f"\033[{code}m{record.levelname:<8}\033[0m" ts = f"\033[2m{ts}\033[0m" else: level = f"{record.levelname:<8}" return f"{ts} {level} {msg}" _handler = logging.StreamHandler() _handler.setFormatter(_ColorFormatter(datefmt="%H:%M:%S")) logging.basicConfig(level=logging.INFO, handlers=[_handler]) log = logging.getLogger("truenas_migrate")