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 行与轮转前完全一致
86 lines
3.1 KiB
Python
86 lines
3.1 KiB
Python
"""日志轮转配置回归测试(不依赖 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()) |