""" geo_tools.utils.logger ~~~~~~~~~~~~~~~~~~~~~~ 统一日志工厂,支持同时输出到控制台和文件。 使用方式 -------- >>> from geo_tools.utils.logger import get_logger >>> logger = get_logger(__name__) >>> logger.info("处理开始") """ from __future__ import annotations import logging import sys from pathlib import Path _LOG_FORMAT = "%(asctime)s | %(levelname)-8s | %(name)s | %(message)s" _DATE_FORMAT = "%Y-%m-%d %H:%M:%S" # 已初始化的 logger 集合,避免重复添加 handler _initialized: set[str] = set() def get_logger( name: str, level: str | None = None, log_file: Path | str | None = None, *, propagate: bool = False, ) -> logging.Logger: """获取(或创建)一个带格式化 handler 的 Logger。 Parameters ---------- name: Logger 名称,通常传入 ``__name__``。 level: 日志等级字符串;``None`` 时读取 ``settings.log_level``。 log_file: 日志文件路径;``None`` 时读取 ``settings``: 若 ``settings.log_to_file`` 为 True,则写到 ``settings.log_dir/geo_tools.log``。 propagate: 是否向父 logger 传播,默认 False(避免重复输出)。 Returns ------- logging.Logger """ # 延迟导入,避免循环依赖 from app.config.settings import settings as _settings if level is None: level = _settings.log_level numeric_level = logging.getLevelName(level.upper()) logger = logging.getLogger(name) logger.propagate = propagate # 已初始化则直接返回,level 可动态调整 if name in _initialized: logger.setLevel(numeric_level) return logger logger.setLevel(numeric_level) formatter = logging.Formatter(_LOG_FORMAT, datefmt=_DATE_FORMAT) # ── 控制台 handler ──────────────────────────────────────── console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(numeric_level) console_handler.setFormatter(formatter) logger.addHandler(console_handler) # ── 文件 handler ────────────────────────────────────────── _resolve_log_file = log_file if _resolve_log_file is None and _settings.log_to_file: _settings.ensure_dirs() _resolve_log_file = _settings.log_dir / "geo_tools.log" if _resolve_log_file is not None: file_path = Path(_resolve_log_file) file_path.parent.mkdir(parents=True, exist_ok=True) file_handler = logging.FileHandler(file_path, encoding="utf-8") file_handler.setLevel(numeric_level) file_handler.setFormatter(formatter) logger.addHandler(file_handler) _initialized.add(name) return logger def set_global_level(level: str) -> None: """动态调整所有 geo_tools 下 logger 的日志等级。 Parameters ---------- level: 目标日志等级,例如 ``"DEBUG"``。 """ numeric = logging.getLevelName(level.upper()) root = logging.getLogger("geo_tools") root.setLevel(numeric) for handler in root.handlers: handler.setLevel(numeric)