Skip to content

fix(log_box/LogMonitor): 修复log_box及LogMonitor在跨零点时采集节点与日志历史丢失的问题 - #626

Merged
AthenaHibou merged 7 commits into
AUTO-MAS-Project:devfrom
AthenaHibou:fix/log-crossday-rotation
Sep 9, 2026
Merged

fix(log_box/LogMonitor): 修复log_box及LogMonitor在跨零点时采集节点与日志历史丢失的问题#626
AthenaHibou merged 7 commits into
AUTO-MAS-Project:devfrom
AthenaHibou:fix/log-crossday-rotation

Conversation

@AthenaHibou

@AthenaHibou AthenaHibou commented Sep 8, 2026

Copy link
Copy Markdown
Member

摘要

  • 修复任务运行期间脚本日志按天滚动(跨零点)后,零点前的运行日志历史与任务报告中采集到的节点详情丢失的问题:日志监控(LogMonitor)与日志采集(log_box)在检测到日志轮转时不再清空已读内容,并会在同目录按 inode 找回被重命名的旧日志、从中断位置续读未读部分
  • 通用日志采集(log_box)新增 rotated_name 参数:日期式滚动命名(如 zzz-od 的 log.txt.YYYY-MM-DD、OK 系的 ok-script.YYYY-MM-DD.log)由各专项声明 strftime 模板,文件系统不提供 inode 时这是唯一兜底;zzz-od / OK-WW / OK-NTE 专项已接入
  • 更新专项适配 Skill:新增「日志轮转补偿」约定——接入前确认脚本滚动方式(重命名式 / 日期式 / 删除重建式),并同步 get_collect 参数说明与常见坑

Sourcery 摘要

修复跨零点日志轮转导致的运行日志和节点详情丢失,并完善不同轮转命名方式的日志采集补偿支持。

新功能:

  • 为通用日志采集增加 rotated_name 配置,支持按日期模板定位轮转日志。

Bug 修复:

  • 修复跨零点日志轮转后 LogMonitor 丢失历史日志、log_box 丢失未读内容及任务报告节点详情的问题。

增强功能:

  • 统一通过 inode 找回被重命名的旧日志并从原偏移续读,同时保留轮转前的监控历史。
  • 为 ZZZ-OD、OK-WW 和 OK-NTE 专项声明日期式日志轮转命名模板。

文档:

  • 补充专项适配指南和 log_box API 中关于日志轮转方式、补偿策略及 rotated_name 的接入说明。
Original summary in English

Sourcery 摘要

防止跨午夜日志轮换导致运行时历史记录丢失,以及已收集的节点详细信息丢失。

新功能:

  • 添加可配置的日期模式回退支持,用于在不提供 inode 信息的文件系统上定位轮换后的日志。

错误修复:

  • 保留午夜前的日志历史记录,并在脚本日志轮换期间恢复未读取的日志内容和任务报告中的节点详细信息。

增强功能:

  • 统一基于 inode 匹配和偏移量续读的轮换日志恢复机制,同时记录轮换要求和集成指南。

文档:

  • 记录日志轮换补偿行为、rotated_name 的用法、支持的轮换模式,以及适配器集成中的常见问题。
Original summary in English

Sourcery 摘要

在午夜日志轮换期间保留运行时日志历史记录和已收集的节点详细信息,同时扩展对基于日期的轮换名称的恢复支持。

新功能:

  • 添加可配置的日期模式回退支持,用于在不提供 inode 信息的文件系统上定位轮换后的日志。

错误修复:

  • 保留午夜前的 LogMonitor 历史记录,并恢复未读取的轮换日志内容,以确保日志轮换后任务报告仍包含完整的节点详细信息。

增强功能:

  • 统一基于 inode 匹配和基于偏移量的续读机制进行轮换日志恢复,并安全处理不支持的删除或截断轮换模式。

文档:

  • 记录日志轮换补偿要求、rotated_name 的使用方式、支持的轮换模式以及适配器集成指南。
Original summary in English

Summary by Sourcery

保留跨零点日志轮转期间的运行日志和节点详情,并完善不同文件系统与命名方式下的轮转日志恢复支持。

New Features:

  • 为通用日志采集增加 rotated_name 配置,支持在无 inode 文件系统上按日期模板定位轮转日志。

Bug Fixes:

  • 修复跨零点日志轮转后 LogMonitor 历史日志、未读日志内容及任务报告节点详情丢失的问题。

Enhancements:

  • 统一通过 inode 定位重命名的旧日志并从原偏移续读,同时保留轮转前的监控历史。
  • 为 ZZZ-OD、OK-WW 和 OK-NTE 日志采集接入日期式轮转命名支持。

Documentation:

  • 补充日志轮转补偿策略、rotated_name 用法及专项适配注意事项。
Original summary in English

Summary by Sourcery

保留跨零点日志轮转期间的运行日志和节点详情,并完善不同文件系统与命名方式下的轮转日志恢复支持。

New Features:

  • 为通用日志采集增加 rotated_name 配置,支持在无 inode 文件系统上按日期模板定位轮转日志。

Bug Fixes:

  • 修复跨零点日志轮转后 LogMonitor 历史日志、未读日志内容及任务报告节点详情丢失的问题。

Enhancements:

  • 统一通过 inode 定位重命名的旧日志并从原偏移续读,同时保留轮转前的监控历史。
  • 为 ZZZ-OD、OK-WW 和 OK-NTE 日志采集接入日期式轮转命名支持。

Documentation:

  • 补充日志轮转补偿策略、rotated_name 用法及专项适配注意事项。
Original summary in English

任务运行期间脚本日志按天滚动(跨零点)后,会话收尾只读得到新文件,滚动前采集的节点详情在任务报告中丢失。缺省与运行日志监控(LogMonitor)同一逻辑:重命名不改变 inode,在同目录找回被轮换的旧文件并从未读 offset 续读,不依赖命名猜测、不误读同名旧残留;文件系统不提供 inode 时仅回退 .bak 通用约定,日期式命名不在通用组件里猜测。zzz-od / OK-WW / OK-NTE 经 get_collect 的 rotated_name 模板声明各自滚动命名,并修正 sink 回调类型标注与实际三参契约一致。
固定路径监控下脚本把日志重命名滚动(如跨零点 log.txt → log.txt.YYYY-MM-DD)时,原实现清空已读累积内容且备份补偿参数无调用方使用,落库历史只剩零点后内容。改为保留滚动前后累积内容,按 inode 在同目录找回被重命名的旧文件并续读未读尾部(不依赖命名猜测);移除无人使用的 bak_log_path 参数。
@sourcery-ai

sourcery-ai Bot commented Sep 8, 2026

Copy link
Copy Markdown

审查者指南

本 PR 通过在 log_box 与 LogMonitor 中保留轮转前状态、按 inode 找回被重命名的旧日志并从原 offset 续读未读尾部,解决跨零点后运行日志历史和报告节点详情丢失;同时为日期式轮转增加显式 rotated_name 配置,并更新三个专项接入及相关文档。

日志轮转恢复时序图

sequenceDiagram
    participant LogMonitor
    participant LogSource
    participant CurrentLog
    participant RotatedLog
    participant Report

    LogMonitor->>CurrentLog: stat()
    LogMonitor->>LogSource: read_new()
    CurrentLog-->>LogSource: file identity changes
    LogSource->>RotatedLog: _find_rotated_file(old_ino)
    RotatedLog-->>LogSource: renamed old file
    LogSource->>RotatedLog: _read_from(offset)
    RotatedLog-->>LogSource: unread tail
    LogSource->>CurrentLog: _read_tail()
    CurrentLog-->>LogSource: new-file lines
    LogSource->>Report: preserve history and append recovered lines
Loading

文件级变更

变更 详情 文件
在日志采集组件中实现跨轮转续读,避免轮转检测清空历史并支持按 inode 或声明模板找回旧日志。
  • 新增 rotated_name strftime 参数并贯穿 get_collectLogCollectLogSource
  • 重命名式轮转按同目录 inode 定位旧文件,从原 offset 续读未读内容;inode 不可用时按日期模板或 .bak 候选兜底。
  • 保留截断重读和会话内增量读取语义。
app/log_box/collect.py
app/log_box/factory.py
app/log_box/sources.py
修复 LogMonitor 跨零点后的历史日志和节点详情丢失,并补偿读取轮转旧文件尾部。
  • 轮转时保留既有 log_contents 与起始状态,不再重置历史。
  • 按旧 inode 找回被重命名日志,从原 offset 消费未读内容后继续读取新日志。
  • 找回旧日志后立即同步回调,避免关键节点结果延迟。
  • 移除旧的显式 .bak 路径参数及调用链。
app/utils/LogMonitor.py
为日期式日志轮转的专项接入显式声明文件名模板。
  • 为 ZZZ-OD 声明 log.txt.%Y-%m-%d 模板。
  • 为 OK-WW 和 OK-NTE 声明 stem.%Y-%m-%d.suffix 模板。
app/task/ZzzOd/AutoProxy.py
app/task/Okww/AutoProxy.py
app/task/OkNte/AutoProxy.py
补充日志轮转接入规范、API 文档和版本变更记录。
  • 说明重命名式、日期式、.bak、删除重建式和截断式轮转的补偿边界。
  • 更新专项适配 Skill 与 log_box API 示例、参数说明和常见陷阱。
  • 记录跨零点日志历史及节点详情丢失修复。
.agents/skills/mas-script-specialized-adapter/SKILL.md
.agents/skills/mas-script-specialized-adapter/references/logbox-api.md
CHANGELOG.md
res/version.json

提示与命令

与 Sourcery 交互

  • 触发新的审查: 在 pull request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 根据审查评论生成 GitHub issue: 回复审查评论,请 Sourcery 根据该评论创建 issue。你也可以回复审查评论并使用 @sourcery-ai issue,根据该评论创建 issue。
  • 生成 pull request 标题: 在 pull request 标题的任意位置写入 @sourcery-ai,即可随时生成标题。你也可以在 pull request 中评论 @sourcery-ai title,随时重新生成标题。
  • 生成 pull request 摘要: 在 pull request 正文中任意位置写入 @sourcery-ai summary,即可在指定位置随时生成 PR 摘要。你也可以在 pull request 中评论 @sourcery-ai summary,随时重新生成摘要。
  • 生成审查者指南: 在 pull request 中评论 @sourcery-ai guide,即可随时重新生成审查者指南。
  • 解决所有 Sourcery 评论: 在 pull request 中评论 @sourcery-ai resolve,即可解决所有 Sourcery 评论。如果你已经处理完所有评论且不想再看到它们,此功能会很有用。
  • 忽略所有 Sourcery 审查: 在 pull request 中评论 @sourcery-ai dismiss,即可忽略所有现有的 Sourcery 审查。如果你想从新的审查开始,这一功能尤其有用——别忘了评论 @sourcery-ai review 以触发新的审查!

自定义使用体验

访问你的控制面板以:

  • 启用或禁用审查功能,例如 Sourcery 生成的 pull request 摘要、审查者指南等。
  • 更改审查语言。
  • 添加、移除或编辑自定义审查说明。
  • 调整其他审查设置。

获取帮助

Original review guide in English

Reviewer's Guide

本 PR 通过在 log_box 与 LogMonitor 中保留轮转前状态、按 inode 找回被重命名的旧日志并从原 offset 续读未读尾部,解决跨零点后运行日志历史和报告节点详情丢失;同时为日期式轮转增加显式 rotated_name 配置,并更新三个专项接入及相关文档。

Sequence diagram for log rotation recovery

sequenceDiagram
    participant LogMonitor
    participant LogSource
    participant CurrentLog
    participant RotatedLog
    participant Report

    LogMonitor->>CurrentLog: stat()
    LogMonitor->>LogSource: read_new()
    CurrentLog-->>LogSource: file identity changes
    LogSource->>RotatedLog: _find_rotated_file(old_ino)
    RotatedLog-->>LogSource: renamed old file
    LogSource->>RotatedLog: _read_from(offset)
    RotatedLog-->>LogSource: unread tail
    LogSource->>CurrentLog: _read_tail()
    CurrentLog-->>LogSource: new-file lines
    LogSource->>Report: preserve history and append recovered lines
Loading

File-Level Changes

Change Details Files
在日志采集组件中实现跨轮转续读,避免轮转检测清空历史并支持按 inode 或声明模板找回旧日志。
  • 新增 rotated_name strftime 参数并贯穿 get_collectLogCollectLogSource
  • 重命名式轮转按同目录 inode 定位旧文件,从原 offset 续读未读内容;inode 不可用时按日期模板或 .bak 候选兜底。
  • 保留截断重读和会话内增量读取语义。
app/log_box/collect.py
app/log_box/factory.py
app/log_box/sources.py
修复 LogMonitor 跨零点后的历史日志和节点详情丢失,并补偿读取轮转旧文件尾部。
  • 轮转时保留既有 log_contents 与起始状态,不再重置历史。
  • 按旧 inode 找回被重命名日志,从原 offset 消费未读内容后继续读取新日志。
  • 找回旧日志后立即同步回调,避免关键节点结果延迟。
  • 移除旧的显式 .bak 路径参数及调用链。
app/utils/LogMonitor.py
为日期式日志轮转的专项接入显式声明文件名模板。
  • 为 ZZZ-OD 声明 log.txt.%Y-%m-%d 模板。
  • 为 OK-WW 和 OK-NTE 声明 stem.%Y-%m-%d.suffix 模板。
app/task/ZzzOd/AutoProxy.py
app/task/Okww/AutoProxy.py
app/task/OkNte/AutoProxy.py
补充日志轮转接入规范、API 文档和版本变更记录。
  • 说明重命名式、日期式、.bak、删除重建式和截断式轮转的补偿边界。
  • 更新专项适配 Skill 与 log_box API 示例、参数说明和常见陷阱。
  • 记录跨零点日志历史及节点详情丢失修复。
.agents/skills/mas-script-specialized-adapter/SKILL.md
.agents/skills/mas-script-specialized-adapter/references/logbox-api.md
CHANGELOG.md
res/version.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

您好——我发现了 1 个问题

给 AI 代理的提示
请处理本次代码审查中的评论:

## 单独评论

### 评论 1
<location path="app/utils/LogMonitor.py" line_range="196-199" />
<code_context>
+                # 同一次运行,log_contents 与 if_log_start 保留不清空,否则
+                # 落库历史只剩轮转后内容(跨零点实测);被重命名的旧文件按
+                # inode 在同目录找回,把未读尾部接回来
                 if (
                     log_stat.st_ino != current_path.stat().st_ino
                     or log_stat.st_size > current_path.stat().st_size
                 ):
+                    if_log_start = await self._recover_rotated_tail(
+                        current_path,
</code_context>
<issue_to_address>
**问题 (bug_risk):** LogMonitor 仅通过 `st_ino`(或文件大小减小)来检测文件替换或轮转。在对每个文件都报告 `st_ino == 0` 的文件系统上,如果替换后的新文件已经增长到超过之前的偏移量,就无法检测到文件替换,因此监控会从旧偏移量开始读取,永久跳过新日志的开头部分。

**触发条件:** 当被监控的日志在 inode 值不可用的文件系统上发生轮转或替换,并且新文件在下一次轮询前增长到超过之前文件的偏移量时。

**建议修复:** 跟踪其他身份字段,例如创建时间/变更时间;或者显式处理 `st_ino == 0`,在重新初始化偏移量之前,通过其他可靠的文件身份信号检测文件是否已被替换。
</issue_to_address>

Sourcery 评估

需要人工审查。 当前有 1 个发现需要优先处理;如果 inode 恢复或修改后的轮转处理选择了错误的文件或偏移量,过时或重复的日志行可能会被追加到任务历史和报告中,而被移除的监控参数可能会导致现有调用方在运行时出错。回滚可以避免进一步影响,但无法移除已经持久化的不正确历史记录或已对外展示的报告。

阻塞性发现:app/utils/LogMonitor.py:199


Sourcery 对开源项目免费——如果您喜欢我们的审查,请考虑分享它们 ✨
Original comment in English

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="app/utils/LogMonitor.py" line_range="196-199" />
<code_context>
+                # 同一次运行,log_contents 与 if_log_start 保留不清空,否则
+                # 落库历史只剩轮转后内容(跨零点实测);被重命名的旧文件按
+                # inode 在同目录找回,把未读尾部接回来
                 if (
                     log_stat.st_ino != current_path.stat().st_ino
                     or log_stat.st_size > current_path.stat().st_size
                 ):
+                    if_log_start = await self._recover_rotated_tail(
+                        current_path,
</code_context>
<issue_to_address>
**issue (bug_risk):** LogMonitor detects replacement or rotation only through `st_ino` (or a size decrease). On filesystems that report `st_ino == 0` for every file, a replacement whose new file has already grown beyond the previous offset is not detected, so monitoring seeks from the old offset and permanently skips the beginning of the new log.

**Triggers:** When the monitored log is rotated or replaced on a filesystem without usable inode values and the new file grows past the previous file offset before the next polling iteration.

**Suggested fix:** Track an additional identity field such as creation/change time, or explicitly handle `st_ino == 0` by detecting replacement through another reliable file identity signal before reinitializing the offset.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 1 finding to address first, and if inode recovery or the revised rollover handling selects the wrong file or offset, stale or duplicated log lines can be appended to task history and reports, and the removed monitor argument could break existing callers at runtime. Reverting prevents further impact but does not remove incorrect history or externally visible reports already persisted.

Blocking findings: app/utils/LogMonitor.py:199


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread app/utils/LogMonitor.py Outdated
Sourcery 审查指出:st_ino 恒为 0 的文件系统(FAT32/exFAT/部分网络盘)上,若新文件在单个轮询周期内长过旧 offset,仅靠 st_ino 比对 + size 缩小检不出替换,监控会从旧 offset 续读新文件、永久跳过其开头。检测身份对齐 LogSource 的 (st_ino, st_ctime_ns) 双信号(Windows 下 ctime 为创建时间,同文件追加不变、被替换才变化),非隧道场景可检出;检出后保留已读历史并从头读取新文件。
@AthenaHibou

Copy link
Copy Markdown
Member Author

@qiyinxi review

@qiyinxi

qiyinxi commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

静态审 + 本地跑了 app/log_box/sources.pyLogMonitor 的真机复现(NTFS,py 3.12),未跑前端。

建议修

  • app/log_box/sources.py:129-131:声明了 rotated_name 之后完全不走 inode 找回,只探测昨天/今天两个名字。昨天那份是上次零点轮转的正常产物、几乎总在;会话内 log.txt 若因删除重建等非重命名原因换了身份(NTFS 上 st_ino 会变,实测会进轮转分支),_read_candidates 就会命中它——self._offset 大于它的大小时按 sources.py:172 归零,把整份昨天日志当成本次运行的新日志推给 push_log 和节点详情;不大于时则从一个无对应关系的字节位置读。同场景不声明模板走 inode 路径只得到新文件内容(['N1'] vs ['OLD-1','OLD-2','OLD-3','N1'])。建议 inode 优先、模板兜底,references/logbox-api.md 里写的也是这个口径(「缺省按 inode 找回;inode 不可用时按 rotated_name 探测」),现在代码和文档是矛盾的。

其他

  • CHANGELOG.md 与 dev 在「社区签到」条目相邻行冲突。解决冲突时请保留 dev 上那条的 by [@Lance0174] 署名并重跑 python scripts/changelog.py sync,否则 res/version.json 会和 CHANGELOG.md 不一致(当前 PR 的 version.json 里该署名已经没了)。dev 自身基线也有两条署名不同步,与本 PR 无关。

核对过没问题的:bak_log_path 全仓零残留调用;轮转分支里 log_stat 无未绑定路径、offset 时序正确无重复摄入(重命名轮转 → ['L0','L1','L1b','L2'],再截断、再删除重建均无重复行);_decode_lines 在旧/新文件各调一次的 offset 计算正确;res/version.json 通过 changelog.py checktests/task/test_oknte_push_log.pytest_zzzod_push_log.pytests/tools/test_log_sign_and_hook.py 37 passed,pytest tests --collect-only -q 退出码 0。

OkNte 的 rotated_nameLogPathFormat 生效时模板嵌的是字面 stem——但那种配置下脚本写的是新日期文件、轮转分支根本不触发,无实际影响,不用改。

PR 审查(真机复现)发现:声明 rotated_name 后完全跳过 inode 找回,会话内日志因删除重建等非重命名原因换身份时会进入轮转分支,候选探测命中昨天残留的轮转产物(上次零点轮转的正常产物几乎总在),offset 夹零时把旧日志整份错当本次运行内容推给任务报告。改为有 inode 一律按 inode 在同目录精确找回,未命中宁缺勿错、声明模板也不猜名字;rotated_name 仅在文件系统不提供 inode(FAT32/exFAT/网络盘)时按昨天/今天生成候选生效,未声明回退 .bak 约定。
@qiyinxi

qiyinxi commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

第二轮(9cad7110)。上轮那条已修到位:声明 rotated_name 后不再绕过 inode 找回,模板只在 st_ino == 0 时兜底,文档口径也跟着改对了。把 head 的 app/log_box/sources.py 导出来用 py 3.12 跑了五个场景(改名轮转 / 删除重建 / 删除重建且同目录有昨天残留并声明模板 / 截断 / 旧文件末行无换行),结果都对,上轮那个「整份昨天日志被当成本次运行内容」复现不出来了。

必修

  • app/utils/LogMonitor.py:199-211:新的 inode 轮转分支没有像 :165 那样在排空旧文件后刷新 last_callback_time,会让 ZZZ-OD 跨零点被误判「运行超时」并中止任务。链条是:旧文件在轮转前那一轮已经读空 → _recover_rotated_tail 一行不返回 → _sync_and_callback:475 只在行数变化时才回调)不触发 → last_callback_time 仍停在昨天 23:59:xx → offset = 0 后读新文件,首行 00:00:00.3update_latest_timestamp:345)用昨天补日期,latest_time 落到 24 小时前 → app/task/ZzzOd/AutoProxy.py:1315datetime.now() - latest_time 直接超过 RunTimeLimit,判「ZZZ-OD 运行超时」、置异常并 wait_event.set();前一条启动器存活检查此时拦不住(启动器正常在跑)。:165 的注释写的就是这个坑,新分支漏了同一处理。受影响的只有 ZZZ-OD(%H:%M:%S.%f 无日期 + 固定路径走新分支):BetterGI 同样无日期但走 :165 的路径切换分支,okww/oknte 时间格式带日期。dev 上不会发生——轮转时 if_log_start 被重置为 False,这些行压根不进 update_latest_timestamp(代价正是本 PR 要修的内容丢失),所以是本 PR 新引入的。

    实跑过:把 head 的 LogMonitor.py 导到临时目录、桩掉 constants/logger/tools、假造 datetime.now(),空尾部场景 now=09-10 00:00:00.5latest_time=09-09 00:00:00.3(差 24h);非空尾部场景两次回调的时间都正确;dev 版同场景 now-latest=3.5scontent=[]。在 :211 后补一行 self.last_callback_time = datetime.now() 重跑,空尾部恢复正常、非空尾部不受影响。

其他(不用改,给维护者)

  • res/version.json 里两条署名的消失是 changelog.py sync 的正常输出(dev 基线本来就差这两条,本 PR 一字未多改)。但合并后 main 上那份旧的 append-version-contributor.yml 会把它们当成新增条目、按本 PR 作者补署一次。fix(ws): BetterGI 与 ZZZ-OD 迁移到 Publisher 出口 #615 合并时已经发生过一轮:森空岛… 那条的署名从 @Lance0174 变成了我。属仓库流程问题。

核对过没问题的:bak_log_path 全仓零残留调用;轮转分支里 log_stat 无未绑定路径、offset 时序正确无重复摄入;有 inode 时去掉 .bak 兜底对现有三个使用方无影响(都不产生 .bak 日志);logbox-api.mdsources.py 口径一致;CI 三项全绿。

静态审 + 上面两处真机复现,未跑仓库测试套件、未跑前端。

PR 审查(真机复现)发现:inode 轮转分支排空旧文件后未像路径切换分支那样刷新 last_callback_time。旧文件在轮转前一轮已读空时找回一行未得、无行触发回调,last_callback_time 停在轮转前;无日期时间格式(ZZZ-OD 的 %H:%M:%S.%f)的跨零点首行经 update_latest_timestamp 按该旧锚补日期,latest_time 落到 24 小时前,check_log 判定运行超时并中止任务(dev 上轮转会重置 if_log_start、这些行不进时间判定,故为本 PR 新引入)。与路径切换分支对齐:找回之后刷新。
@qiyinxi

qiyinxi commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

第三轮(410ab9a4)。上轮那条必修已改到位,位置和顺序都对:刷新落在 _recover_rotated_tailoffset = 0 之后,旧文件找回的行仍按轮转前的日期解析,新文件的行按今天解析。没有要改的地方。

实跑(py 3.12 / NTFS,把三个版本的 LogMonitor.py 导到临时目录、桩掉 constants/logger/tools、假造 datetime.now()):

  • 跨零点改名轮转、旧文件空尾部:本 head now - latest_time = 0.2s;上一 head 9cad7110 同场景是 1 day 0:00:00.2,会触发 app/task/ZzzOd/AutoProxy.py:1315 的「ZZZ-OD 运行超时」。
  • 跨零点改名轮转、旧尾部有一行未读:三次回调依次 09-09 23:59:50.1 / 09-09 23:59:59.9 / 09-10 00:00:00.3,历史完整、无重复行。
  • 非跨零点进入同一分支(白天截断、跨零点截断、跨零点删除重建):now - latest_time 均为 0,再空推 10 分钟仍能正常判出超时——刷新不会掩盖停滞,latest_time 只在读到新行时才变。

顺带更正我上一轮的一句话:「受影响的只有 ZZZ-OD」范围写窄了。OK-NTE(app/task/OkNte/AutoProxy.py:361)和通用脚本(app/task/general/AutoProxy.py:273)的时间格式由用户配置,只要填的是无日期格式、日志又按改名轮转,同样吃这处修复。BetterGI 走路径切换分支(app/utils/LogMonitor.py:165 已有刷新),Okww / MAA / M9A / SRC / MaaEnd 格式带日期,都不受影响。

核对过没问题的:last_callback_time 全仓只在 LogMonitor.py 内读写,不参与停滞与节流判定(那用 last_callback_at / latest_progress_at),刷新它只影响后续行的日期补全;app/log_box/sources.py 无 strptime,不存在同类日期基准问题;monitor_file 其余几条 continue 路径要么已调 do_callback、要么在 offset 归零后进不去;scripts/changelog.py check 在本 head 退出码 0。

静态审 + 上述实跑,未跑仓库测试套件、未跑前端。

@AthenaHibou
AthenaHibou merged commit 1c071a3 into AUTO-MAS-Project:dev Sep 9, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants