108 lines
3.2 KiB
Python
108 lines
3.2 KiB
Python
"""
|
||
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)
|