feat: 日志轮转(保留 14 天)
main.log 无轮转、已涨到 34MB,且每晚任务都会继续追加。 - logrotate/xwlb 模板 + scripts/install_logrotate.sh(幂等,装到 /etc/logrotate.d/xwlb) - 每天轮转、保留 14 份、立即压缩 → main.log-YYYYMMDD.gz - 必须用 copytruncate:脚本以 >>main.log 长开句柄,改名式轮转会 让当晚日志写进归档、新文件空着 - journald 同步设 MaxRetentionSec=14day + SystemMaxUse=500M - 安装脚本自带 logrotate -d 校验(这条立刻抓到"指令行尾注释"被当成参数值的错误) - tests/test_logrotate.py 19 项,锁住保留天数、行尾注释、占位符替换 实测:34MB → 2.9MB,归档 32453 行与轮转前完全一致
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
"""日志轮转配置回归测试(不依赖 pytest,不需要 root)
|
||||
|
||||
python tests/test_logrotate.py
|
||||
|
||||
覆盖:
|
||||
- 保留 14 天等关键指令齐全;
|
||||
- **指令后面不能有行尾注释**(logrotate 会把注释当成参数值,
|
||||
实测报错 `bad rotation count '14 # 保留 14 份'`,这条就是那次踩坑的回归);
|
||||
- 占位符齐全,且安装脚本确实会替换(否则装过去的路径是字面量 @PROJECT_DIR@);
|
||||
- 若本机已安装,装好的文件里不应残留占位符。
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
CONF = ROOT / 'logrotate' / 'xwlb'
|
||||
INSTALLER = ROOT / 'scripts' / 'install_logrotate.sh'
|
||||
INSTALLED = Path('/etc/logrotate.d/xwlb')
|
||||
|
||||
|
||||
def directives(text):
|
||||
"""取配置块里的指令行(去注释、去空行、去大括号)"""
|
||||
out = []
|
||||
for raw in text.splitlines():
|
||||
line = raw.strip()
|
||||
if not line or line.startswith('#') or line in ('{', '}'):
|
||||
continue
|
||||
out.append(line)
|
||||
return out
|
||||
|
||||
|
||||
def main():
|
||||
failed = 0
|
||||
|
||||
def check(desc, got, want):
|
||||
nonlocal failed
|
||||
if got == want:
|
||||
print(f" ✓ {desc}: {got!r}")
|
||||
else:
|
||||
failed += 1
|
||||
print(f" ✗ {desc}: 期望 {want!r},实际 {got!r}")
|
||||
|
||||
print("配置文件:")
|
||||
check('模板存在', CONF.exists(), True)
|
||||
text = CONF.read_text(encoding='utf-8')
|
||||
lines = directives(text)
|
||||
|
||||
def has_directive(name):
|
||||
return any(re.match(rf'^{name}(\s|$)', ln) for ln in lines)
|
||||
|
||||
for d in ('daily', 'rotate', 'missingok', 'notifempty', 'compress',
|
||||
'copytruncate', 'dateext', 'dateformat', 'su'):
|
||||
check(f'含指令 {d}', has_directive(d), True)
|
||||
|
||||
print("\n保留天数:")
|
||||
rotate = [ln for ln in lines if ln.startswith('rotate')][0]
|
||||
check('rotate 值为 14(保留 14 天)', int(rotate.split()[1]), 14)
|
||||
|
||||
print("\n行尾注释(logrotate 会把注释吃成参数值):")
|
||||
bad = [ln for ln in lines if '#' in ln]
|
||||
check('指令行无行尾注释', bad, [])
|
||||
|
||||
print("\n占位符与安装替换:")
|
||||
check('含路径占位符 @PROJECT_DIR@', '@PROJECT_DIR@' in text, True)
|
||||
check('含属主占位符 @OWNER@', '@OWNER@' in text, True)
|
||||
installer = INSTALLER.read_text(encoding='utf-8')
|
||||
check('安装脚本会替换路径占位符', 's#@PROJECT_DIR@#' in installer, True)
|
||||
check('安装脚本会替换属主占位符', 's#@OWNER@#' in installer, True)
|
||||
check('安装脚本会做语法校验(logrotate -d)', 'logrotate -d' in installer, True)
|
||||
|
||||
print("\n已安装文件(若本机装过):")
|
||||
if INSTALLED.exists():
|
||||
installed = INSTALLED.read_text(encoding='utf-8')
|
||||
check('无残留占位符', [p for p in ('@PROJECT_DIR@', '@OWNER@', '@GROUP@')
|
||||
if p in installed], [])
|
||||
check('已指向真实项目路径', str(ROOT) in installed, True)
|
||||
else:
|
||||
print(" · 本机未安装,跳过(未运行 scripts/install_logrotate.sh)")
|
||||
|
||||
print(f"\n结果: {'全部通过' if failed == 0 else f'{failed} 项失败'}")
|
||||
return 1 if failed else 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user