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:
2026-09-25 12:50:11 +08:00
parent b996b444c4
commit 1d3ba02703
7 changed files with 226 additions and 5 deletions
+9
View File
@@ -294,6 +294,8 @@ xwlb/
| **定时任务(生产入口)** | `bash scripts/run_daily.sh [YYYYMMDD]` | 全链路 + 失败重试(21:30 / 22:00);由 `systemd/xwlb-daily.timer` 每天 21:00 触发 |
| 这天跑到哪一步 | `python scripts/day_status.py <日期>` | 退出码 0=已有精编 / 1=识别完成只缺切分 / 2=需重跑全链路 |
| 安装定时任务 | `bash scripts/install_systemd.sh` | 安装单元、启用定时器与隧道服务 |
| 安装日志轮转 | `bash scripts/install_logrotate.sh` | 装 `/etc/logrotate.d/xwlb`(每天/保留14天/压缩)与 journald 上限(14 天) |
| 校验轮转配置 | `sudo logrotate -d /etc/logrotate.d/xwlb` | 干跑,不实际轮转 |
| 校对效果评估 | `python tools/compare_raw_improve.py --md <文件>` | 统计 news_raw vs news_improve 差异分类与 token 开销 |
| 合并方案评估 | `python tools/experiment_merge_correct_split.py <日期>` | 实测"校对+切分合并成一次调用"的覆盖率与事实漂移 |
| 配置自检 | `python tests/test_config.py` | 校验 config.yml 加载、优先级与敏感项隔离 |
@@ -341,6 +343,13 @@ xwlb/
退出码约定:`0` 成功 / `1` 重试用尽仍失败 / `2` 已有实例在运行。
**日志(保留 14 天)**:任务日志 `main.log` 由 logrotate 每天轮转为
`main.log-YYYYMMDD.gz` 保留 14 份(`logrotate/xwlb` 模板 →
`scripts/install_logrotate.sh` 安装);systemd journal 通过
`/etc/systemd/journald.conf.d/xwlb.conf` 设 `MaxRetentionSec=14day` + `SystemMaxUse=500M`。
轮转必须用 `copytruncate`——脚本以 `>>main.log` 长开句柄,
改名式轮转会让当晚日志写进归档、新文件空着。
**为什么必须有 `state/.asr_complete_<日期>` 标记**:识别中途卡死(B19)会留下部分分片,
若只按"库里有没有分片"判断,重试会走"只重跑切分",把**半天内容当成完整一天**入库且不报错。
标记由 `audioRead.process_long_audio` 在**全部识别成功**时写入,失败时主动删除。
+5 -2
View File
@@ -19,7 +19,7 @@
| B3 | P0 | `newsProcess.py:89,119-121` | DeepSeek 返回非纯 JSON(```json 围栏 / 截断)即整批失败 | 22 天 `xwlb_daily_ext` 当天 0 条 | ✅ 已修复(回归测试覆盖) |
| B4 | P0 | `getVideo5.py:69,92,156` | `get_xwlb_video_link()` 返回值错误(只回最后一个 / 未定义 / `[]`) | 取错链接、`UnboundLocalError`、无效 URL 仍进下载 | ✅ 已修复并实测(联网) |
| B5 | P0 | `getVideo5.py:104-146`、`audioRead.py:389-399` | 下载与分片落库无幂等 | 重跑 → 重复下载 + `xwlb_daily` 重复行 → 新闻重复、token 翻倍 | ✅ 已修复并实测 |
| B6 | P1 | `audioRead.py:260`、`getVideo5.py:132-140` | 关键外部调用无 timeout/重试;ffmpeg 输出未接管 | 单点失败无补救;`main.log` 34MB 且无轮转 | 🟡 部分(校对已加超时/重试;ffmpeg 输出已接管;yt-dlp 进度与日志轮转未处理) |
| B6 | P1 | `audioRead.py:260`、`getVideo5.py:132-140` | 关键外部调用无 timeout/重试;ffmpeg 输出未接管 | 单点失败无补救;`main.log` 34MB 且无轮转 | 🟡 部分(校对已加超时/重试;ffmpeg 输出已接管;**日志轮转已完成**;yt-dlp 进度未处理) |
| B7 | P1 | `deepseek.py:257-268` | 降级分支 `return result`(变量错/可能未绑定) | DeepSeek 失败时抛 `NameError`,降级形同虚设 | ✅ 已修复(`return fallback_result`) |
| B8 | P1 | `audioRead.py:422-439` | `__main__` 示例用 3 参数调 4 参数签名 | `python audioRead.py` 必错 | ✅ 已修复(改为 `python audioRead.py [YYYYMMDD]`) |
| B9 | P1 | `audioRead.py:387`、`getVideo5.py:104-146` | 临时文件不清理(chunk/input.wav/mp4) | 单日约 180MB 常驻,磁盘持续增长 | 🟡 部分(分片与按日 WAV 已在 `finally` 清理;mp4/mp3 保留策略待定) |
@@ -345,7 +345,10 @@ systemd `TimeoutStartSec=10800`。**根治需要给 ASR 加子进程看门狗**
## 后续待办(本轮未做)
1. **数据治理**:`xwlb_daily` 现存 101 行空白记录(可识别、可重跑修复);725 天原文里 203 天没有 `xwlb_daily_ext`——回填需重新调用 DeepSeek,属于有成本的操作,需你确认范围后再执行。
2. **B6 收尾**:yt-dlp 进度 hook 仍直接 `print`;`main.log` 无轮转(建议 `RotatingFileHandler` 或由外部 logrotate 管理)。
2. **B6 收尾**:yt-dlp 进度 hook 仍直接 `print`;
~~`main.log` 无轮转~~ —— **已完成(2026-09-25)**:logrotate 每天轮转、保留 14 天、立即压缩
(`logrotate/xwlb` + `scripts/install_logrotate.sh`),journal 同步设 `MaxRetentionSec=14day`;
实测 34MB → 2.9MB 且归档行数与轮转前一致(32453 行)。
3. **B9 收尾**:`xwlb_video/*.mp4` 与 `*.mp3` 是否保留需定策略(mp3 可重跑,mp4 价值较低)。
4. ~~**B14 收尾**:`git init` 纳入版本管理~~ —— **已完成(2026-09-25)**:
已建仓并推送到 `git@192.168.1.10:simon/xwlb.git`(分支 `main`),