Files
tick-stock-panel/backend/tests/test_logging_setup.py
T
shy3130 5d6ac5c736 fix(logs): 日志归拢 data/logs/ 并为桌面版 desktop.log 增加大小切割
- 新增 app/logfile.py attach_rotating_log: 统一 data/logs/ 子目录 + RotatingFileHandler + 旧位置日志一次性迁移 (目标已存在/被占用则跳过, 失败仅 warning 不阻断启动)
- desktop.py: desktop.log 由无限追加 FileHandler 改为 10MB×5 切割 (长期挂机不再无限增长); 失败页与错误提示的用户可见路径同步 data\logs\desktop.log
- main.py: backend.log 改用共享函数, 参数不变 (10MB×3), 目录随迁
- README/dev.ps1 排障路径同步; 新增 test_logging_setup.py 4 例 (迁移/冷启动/不覆盖/不可写容错)
2026-10-23 22:00:51 +08:00

85 lines
3.4 KiB
Python

"""app/logfile.py: 日志归拢 data/logs/ + 大小切割 + 旧位置一次性迁移。"""
from __future__ import annotations
import logging
from logging.handlers import RotatingFileHandler
import pytest
from app.logfile import attach_rotating_log
from app.config import settings
@pytest.fixture()
def data_dir(tmp_path, monkeypatch):
monkeypatch.setattr(settings, "data_dir", tmp_path, raising=False)
return tmp_path
@pytest.fixture()
def no_leaked_handlers():
"""测试后摘掉本用例新挂的 root handler, 并 close 释放文件句柄。
RotatingFileHandler 构造即打开日志文件, 不 close 的话 Windows 上
tmp_path 清理会因文件占用失败, 且 handler 会泄漏到后续用例。
"""
before = {id(h) for h in logging.getLogger().handlers}
yield
for handler in list(logging.getLogger().handlers):
if id(handler) not in before:
logging.getLogger().removeHandler(handler)
handler.close()
def test_attaches_rotating_handler_and_migrates_legacy(data_dir, no_leaked_handlers):
# 旧位置: data 根下的 backend.log 与轮转副本 .1
(data_dir / "backend.log").write_text("old-main", encoding="utf-8")
(data_dir / "backend.log.1").write_text("old-rot", encoding="utf-8")
attach_rotating_log("backend.log", max_mb=10, backup_count=3)
# 旧文件被搬进 logs/, 内容不丢
assert (data_dir / "logs" / "backend.log").read_text(encoding="utf-8") == "old-main"
assert (data_dir / "logs" / "backend.log.1").read_text(encoding="utf-8") == "old-rot"
assert not (data_dir / "backend.log").exists()
assert not (data_dir / "backend.log.1").exists()
# handler 为轮转式, 参数符合调用
handlers = [h for h in logging.getLogger().handlers if isinstance(h, RotatingFileHandler)]
assert handlers
added = handlers[-1]
assert added.maxBytes == 10 * 1024 * 1024
assert added.backupCount == 3
def test_no_legacy_files_starts_fresh(data_dir, no_leaked_handlers):
attach_rotating_log("desktop.log", max_mb=10, backup_count=5)
# RotatingFileHandler 构造即以追加模式打开目标文件
assert (data_dir / "logs" / "desktop.log").exists()
assert not (data_dir / "desktop.log").exists()
def test_existing_target_not_overwritten(data_dir, no_leaked_handlers):
# logs/ 里已有同名文件 (如上一轮迁移过) → 旧位置的文件原地保留, 不覆盖
(data_dir / "logs").mkdir()
(data_dir / "logs" / "backend.log").write_text("new", encoding="utf-8")
(data_dir / "backend.log").write_text("old", encoding="utf-8")
attach_rotating_log("backend.log", max_mb=10, backup_count=3)
assert (data_dir / "logs" / "backend.log").read_text(encoding="utf-8") == "new"
assert (data_dir / "backend.log").read_text(encoding="utf-8") == "old"
def test_unwritable_data_dir_only_warns(tmp_path, monkeypatch, caplog, no_leaked_handlers):
# data_dir 指向一个普通文件 → mkdir 失败 → 只 warning, 不抛异常、不加 handler
blocker = tmp_path / "not-a-dir"
blocker.write_text("", encoding="utf-8")
monkeypatch.setattr(settings, "data_dir", blocker, raising=False)
before_count = len(logging.getLogger().handlers)
with caplog.at_level(logging.WARNING):
attach_rotating_log("backend.log", max_mb=10, backup_count=3)
assert "文件日志初始化失败" in caplog.text
assert len(logging.getLogger().handlers) == before_count