refactor: 重构项目结构,将geo_tools重命名为app并更新相关引用

- 将主包名从geo_tools改为app
- 更新所有模块中的引用路径
- 迁移并更新测试用例
- 添加项目规则文档
- 保持原有功能不变,仅进行结构调整
This commit is contained in:
2026-04-12 19:49:56 +08:00
parent fcb8e1f255
commit db51d41aef
41 changed files with 4132 additions and 808 deletions

107
app/utils/logger.py Normal file
View File

@@ -0,0 +1,107 @@
"""
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)