f7a7a49281
免确认授权已开启判定加 authorization_id 非空,与打款一致 --------- Co-authored-by: guke <guke@autohome.com.cn> Reviewed-on: #168
74 lines
2.9 KiB
Python
74 lines
2.9 KiB
Python
"""SafeRotatingFileHandler:Windows 轮转不被外部句柄卡死(WinError 32)。
|
|
|
|
stdlib RotatingFileHandler 靠 rename 活动文件轮转;Windows 上只要有第二个句柄(admin
|
|
第二进程、残留 --reload worker、IDE 索引、杀软)开着它, rename 就 WinError 32、轮转永久
|
|
卡死。Safe 版在 Windows 改走 copytruncate(拷贝→就地清空, 从不 rename 活动文件)。
|
|
|
|
这些用例用 monkeypatch 把 os.name 强制成 "nt", 使 copytruncate 分支在任何 OS 的 CI 上都被
|
|
覆盖;在真实 Windows 上则天然命中。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from pathlib import Path
|
|
|
|
from app.core.logging import SafeRotatingFileHandler
|
|
|
|
|
|
def _emit(handler: logging.Handler, msg: str) -> None:
|
|
handler.emit(logging.LogRecord("t", logging.INFO, __file__, 0, msg, (), None))
|
|
|
|
|
|
def test_rollover_survives_second_open_handle(tmp_path: Path, monkeypatch) -> None:
|
|
"""第二个句柄开着活动文件时轮转:不抛异常, 且确实转了(生成 .1、活动文件就地清空)。"""
|
|
monkeypatch.setattr("app.core.logging.os.name", "nt")
|
|
|
|
log_file = tmp_path / "app-server.log"
|
|
# maxBytes 放大, 避免 emit 期间自动轮转干扰;本用例手动触发 doRollover。
|
|
handler = SafeRotatingFileHandler(
|
|
str(log_file), maxBytes=10**9, backupCount=3, encoding="utf-8",
|
|
)
|
|
handler.setFormatter(logging.Formatter("%(message)s"))
|
|
try:
|
|
for i in range(20):
|
|
_emit(handler, f"line-{i:03d}")
|
|
handler.flush()
|
|
before = log_file.stat().st_size
|
|
assert before > 0
|
|
|
|
# 正是 Windows 上 rename 失败的条件:另一个句柄开着活动文件。
|
|
with open(log_file, "a", encoding="utf-8"):
|
|
handler.doRollover() # 不应抛 PermissionError / WinError 32
|
|
|
|
backup = tmp_path / "app-server.log.1"
|
|
assert backup.exists()
|
|
assert backup.stat().st_size == before # 轮转前内容完整进了备份
|
|
assert log_file.stat().st_size == 0 # 活动文件就地清空(不是 rename)
|
|
|
|
# 句柄没被 rename 破坏, 仍能继续写。
|
|
_emit(handler, "after-rollover")
|
|
handler.flush()
|
|
assert log_file.stat().st_size > 0
|
|
finally:
|
|
handler.close()
|
|
|
|
|
|
def test_backups_shift_and_capped(tmp_path: Path, monkeypatch) -> None:
|
|
"""多次轮转:.1/.2 逐级腾挪, 超过 backupCount 的丢弃(不出现 .3)。"""
|
|
monkeypatch.setattr("app.core.logging.os.name", "nt")
|
|
|
|
log_file = tmp_path / "app-server.log"
|
|
handler = SafeRotatingFileHandler(
|
|
str(log_file), maxBytes=10**9, backupCount=2, encoding="utf-8",
|
|
)
|
|
handler.setFormatter(logging.Formatter("%(message)s"))
|
|
try:
|
|
for _ in range(4):
|
|
_emit(handler, "x" * 50)
|
|
handler.doRollover()
|
|
assert (tmp_path / "app-server.log.1").exists()
|
|
assert (tmp_path / "app-server.log.2").exists()
|
|
assert not (tmp_path / "app-server.log.3").exists()
|
|
finally:
|
|
handler.close()
|