75 lines
2.1 KiB
Python
75 lines
2.1 KiB
Python
"""Shared UTF-8 logging helpers for OCR scripts."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import re
|
|
import sys
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
|
|
def safe_log_stem(value: str) -> str:
|
|
cleaned = re.sub(r"[^\w.-]+", "_", value, flags=re.UNICODE).strip("._")
|
|
return cleaned or "ocr"
|
|
|
|
|
|
def default_log_path(
|
|
project_root: Path,
|
|
category: str,
|
|
stem: str,
|
|
*,
|
|
device: str | None = None,
|
|
) -> Path:
|
|
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
suffix = f"-{safe_log_stem(device)}" if device else ""
|
|
filename = f"{safe_log_stem(stem)}{suffix}-{timestamp}.log"
|
|
return project_root / "logs" / safe_log_stem(category) / filename
|
|
|
|
|
|
def setup_run_logger(
|
|
name: str,
|
|
log_file: Path,
|
|
*,
|
|
verbose: bool = False,
|
|
console: bool = True,
|
|
) -> logging.Logger:
|
|
"""Create an isolated logger that writes UTF-8 text and optional console output."""
|
|
log_file = log_file.expanduser().resolve()
|
|
log_file.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
logger = logging.getLogger(name)
|
|
logger.setLevel(logging.DEBUG if verbose else logging.INFO)
|
|
logger.propagate = False
|
|
for handler in logger.handlers[:]:
|
|
handler.close()
|
|
logger.removeHandler(handler)
|
|
|
|
formatter = logging.Formatter(
|
|
fmt="%(asctime)s | %(levelname)-8s | pid=%(process)d | %(message)s",
|
|
datefmt="%Y-%m-%d %H:%M:%S",
|
|
)
|
|
|
|
file_handler = logging.FileHandler(log_file, encoding="utf-8")
|
|
file_handler.setLevel(logging.DEBUG)
|
|
file_handler.setFormatter(formatter)
|
|
logger.addHandler(file_handler)
|
|
|
|
if console:
|
|
console_handler = logging.StreamHandler(sys.stdout)
|
|
console_handler.setLevel(logging.DEBUG if verbose else logging.INFO)
|
|
console_handler.setFormatter(formatter)
|
|
logger.addHandler(console_handler)
|
|
|
|
logger.info("LOG_INITIALIZED file=%s", log_file)
|
|
return logger
|
|
|
|
|
|
def close_logger(logger: logging.Logger) -> None:
|
|
for handler in logger.handlers[:]:
|
|
try:
|
|
handler.flush()
|
|
handler.close()
|
|
finally:
|
|
logger.removeHandler(handler)
|