SHA256
P0: - 清理 src/fetchers/sector.py 第 225 行起的旧版残留代码 - 修复 src/fetchers/market_daily.py 中 fetch_history -> _fetch_history 笔误 - 在 src/main.py 挂载 --market-daily 子命令 P1: - 修正 src/db.py docstring(market_breadth -> market_daily 等) - requirements.txt 补 baostock;pyproject.toml 同步 + 新增 [dev] extras - README 增加 config.yaml 安全提示,将 git 历史清理升级为高风险 P0 由用户决策 P2: - 引入 src/log.py 统一 logging(控制台 + logs/ashare.log 按日滚动 7 天) - 建立 tests/ 框架,4 个测试文件 / 19 个 pytest 用例全部通过 - pyproject.toml 新增 [tool.pytest.ini_options] - config.example.yaml 补全 workers 字段与多源说明 - README 更新功能概览/参数说明/表结构/项目结构/设计说明全章节 - TODO.md 全面重写,按 P0/P2 整理剩余条目并附变更日志 - .gitignore 新增 .claude/
59 lines
1.7 KiB
Python
59 lines
1.7 KiB
Python
"""统一日志模块
|
||
|
||
所有 fetcher 应通过 `from src.log import get_logger` 获取 logger,避免散落的 print。
|
||
- 控制台默认 INFO;可通过环境变量 ASHARE_LOG_LEVEL 调整 (DEBUG/INFO/WARNING/ERROR)。
|
||
- 同时输出到 logs/ashare.log,按日滚动,保留 7 天。
|
||
- 出于兼容考虑,旧 print 调用本期保留,新代码请用 logger。
|
||
"""
|
||
|
||
import logging
|
||
import os
|
||
import sys
|
||
from logging.handlers import TimedRotatingFileHandler
|
||
from pathlib import Path
|
||
|
||
_INITIALIZED = False
|
||
_DEFAULT_FORMAT = "%(asctime)s [%(levelname)s] [%(name)s] %(message)s"
|
||
|
||
|
||
def _init_root() -> None:
|
||
global _INITIALIZED
|
||
if _INITIALIZED:
|
||
return
|
||
|
||
level_name = os.environ.get("ASHARE_LOG_LEVEL", "INFO").upper()
|
||
level = getattr(logging, level_name, logging.INFO)
|
||
|
||
root = logging.getLogger("ashare")
|
||
root.setLevel(level)
|
||
root.propagate = False
|
||
|
||
fmt = logging.Formatter(_DEFAULT_FORMAT, datefmt="%Y-%m-%d %H:%M:%S")
|
||
|
||
sh = logging.StreamHandler(sys.stdout)
|
||
sh.setFormatter(fmt)
|
||
root.addHandler(sh)
|
||
|
||
log_dir = Path(os.environ.get("ASHARE_LOG_DIR", "logs"))
|
||
try:
|
||
log_dir.mkdir(parents=True, exist_ok=True)
|
||
fh = TimedRotatingFileHandler(
|
||
log_dir / "ashare.log",
|
||
when="midnight",
|
||
backupCount=7,
|
||
encoding="utf-8",
|
||
)
|
||
fh.setFormatter(fmt)
|
||
root.addHandler(fh)
|
||
except OSError:
|
||
# 文件系统只读或权限不足时只打印到控制台
|
||
pass
|
||
|
||
_INITIALIZED = True
|
||
|
||
|
||
def get_logger(name: str) -> logging.Logger:
|
||
"""返回带统一格式的 logger(命名空间 ashare.<name>)"""
|
||
_init_root()
|
||
return logging.getLogger(f"ashare.{name}")
|