Skip to content

babynata/cron-state-manager

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Cron State Manager

生产级 OpenClaw 定时任务状态管理与日志监控框架

License: MIT


项目背景

在 OpenClaw 生态中,cron 定时任务是最常用的自动化手段——每日推送、状态追踪、定时提醒。但生产运行中总会遇到这些问题:

  • 任务执行了,但消息没收到——不知道是脚本崩了还是通道断了
  • 数据状态错乱——主 session 正在写 state,Worker 同时在读,JSON 损坏
  • 连续失败没人知道——连续 16 次失败后才发现,已经晚了
  • 排查靠猜——没有日志、没有状态聚合、没有告警

Cron State Manager 就是为解决这个问题而生:一个可直接复制使用的生产框架,覆盖状态管理、并发安全、统一日志、故障排查全链路。

设计目标

目标 实现方式
可追溯 每次执行记录完整 stdout/stderr/delivery
可监控 cron-summary.json 聚合所有任务状态
可告警 连续失败 ≥3 次自动标记
可追踪 message() 发送状态单独记录
高可用 原子读写 + 失败降级,不崩主流程

快速开始

1. 复制到项目

# 复制脚本
cp scripts/cron_logger.py ~/.openclaw/workspace/scripts/
cp scripts/state_tool.py ~/.openclaw/workspace/scripts/

# 创建日志目录
mkdir -p ~/.openclaw/workspace/logs
mkdir -p ~/.openclaw/workspace/state

2. Cron Job 配置规范

每个 cron job 必须包含以下字段:

{
  "delivery": {
    "channel": "openclaw-weixin",
    "to": "<user-id>",
    "accountId": "<account-id>"
  },
  "payload": {
    "timeoutSeconds": 180
  }
}

说明:

  • delivery.channel + delivery.to:显式指定目标通道(多通道时必需)
  • delivery.accountId:指定通道账号(多账号/多通道时必需,避免 isolated session 用错实例)
  • payload.timeoutSeconds:至少 180 秒(脚本执行可能很快,但 message() 调用可能因通道延迟而超时)

2. 在 cron 脚本中集成日志

import sys, os, io, time
sys.path.insert(0, os.path.dirname(__file__))
from cron_logger import log_execution

if __name__ == "__main__":
    job_id = "my-task"
    cmd = " ".join(sys.argv)
    start = time.time()
    
    old_stdout = sys.stdout
    old_stderr = sys.stderr
    sys.stdout = io.StringIO()
    sys.stderr = io.StringIO()
    
    try:
        # 你的业务逻辑
        print("Hello, cron!")
        status = "success"
    except Exception:
        import traceback
        traceback.print_exc()
        status = "error"
    finally:
        stdout = sys.stdout.getvalue()
        stderr = sys.stderr.getvalue()
        sys.stdout = old_stdout
        sys.stderr = old_stderr
        
        print(stdout, end="")
        if stderr:
            print(stderr, end="", file=sys.stderr)
        
        duration_ms = int((time.time() - start) * 1000)
        log_execution(job_id, cmd, status, stdout, stderr, duration_ms)

3. 在 cron payload 中增加 delivery 追踪

1. 运行脚本获取内容
2. 发送结果到目标通道
3. 拿到 message() 返回的 messageId 后,运行:
   python3 scripts/cron_logger.py delivery <job_id> <messageId> [status]

4. 查看运行状态

# 查看所有任务状态
python3 -c "
import sys, os
sys.path.insert(0, os.path.expanduser('~/.openclaw/workspace/scripts'))
from cron_logger import get_summary
import json
print(json.dumps(get_summary(), indent=2, ensure_ascii=False))
"

# 查看告警
python3 -c "
import sys, os
sys.path.insert(0, os.path.expanduser('~/.openclaw/workspace/scripts'))
from cron_logger import get_alerts
import json
alerts = get_alerts()
print(json.dumps(alerts, indent=2, ensure_ascii=False) if alerts else '无告警')
"

# 查看某任务详细日志
tail -30 ~/.openclaw/workspace/logs/<job-id>.log

5. 有状态任务的原子读写

from state_tool import read_state, write_state, update_state

# 读
state = read_state("water-tracker")  # 返回 dict 或 None

# 写
write_state("water-tracker", {"date": "2026-05-26", "total": 0})

# 原子更新
update_state("water-tracker", lambda s: {
    **s,
    "records": s["records"] + [{"time": "12:00", "amount": 300}],
    "total": s["total"] + 300
})

架构概览

┌─────────────┐     ┌──────────────┐     ┌─────────────┐
│   Gateway   │────▶│  Cron Worker │────▶│  Python脚本 │
│  (调度器)    │     │  (isolated)  │     │ (业务逻辑)  │
└─────────────┘     └──────────────┘     └──────┬──────┘
                                                  │
                    ┌──────────────┐              │
                    │  主 Session   │◀─────────────┘
                    │ (调度+汇报)   │   用户回复 → 写 state
                    └──────────────┘

核心原则:

  • 每个任务一个 .py 脚本——禁止 payload 内联逻辑
  • 脚本必须集成 cron_logger.py——捕获 stdout/stderr
  • 主 session 写,Worker 读——单向通道,Worker 绝不写 state
  • 原子替换,永不损坏——temp → fsync → LOCK_EX → rename

目录结构

cron-state-manager/
├── SKILL.md                          # OpenClaw Skill 规范
├── README.md                         # 本文件
├── references/
│   ├── architecture.md               # 完整架构时序图 + 并发安全分析
│   ├── cron-logger-guide.md          # 统一日志系统完整指南
│   ├── state-schemas.md              # 通用 Schema 规范 + 新建步骤
│   └── troubleshooting.md            # 故障排查清单
└── scripts/
    ├── cron_logger.py                # 日志记录器(execution + summary + alerts + delivery)
    └── state_tool.py                 # 原子读写工具(fcntl 锁)

功能详解

统一日志系统

类型 文件 说明
Execution Log logs/{job_id}.log 每次执行的完整 stdout/stderr/耗时
Summary logs/cron-summary.json 聚合所有任务的运行状态
Alerts logs/cron-alerts.json 连续失败 ≥3 次自动告警
Delivery logs/{job_id}.log 追加 message() 发送状态追踪

并发安全

  • :temp → fsync → LOCK_EX → rename → 释放锁
  • :LOCK_SH → 读取 → 释放 → 重试 3 次
  • 降级:读失败返回 None,写失败记录日志不抛异常

三层状态分离

层级 存储 读写方 说明
无状态任务 脚本内嵌配置 Worker 自给自足 每次独立运行
有状态任务 state/*.json 主写、Worker 读 跨周期数据传递
人类记忆 memory/*.md 主 session 独占 Worker 不触碰

故障排查速查表

现象 排查步骤
推送没收到 1. 查 logs/{job_id}.log 的 STATUS 2. 查末尾 DELIVERY 段落 3. 查 delivery.accountId 是否正确 4. 查 cron-summary.json
日志 sent 但用户没收到 1. 在微信搜索消息关键词 2. 检查 delivery.accountId 是否与主 session 一致 3. 手动发送测试确认通道正常
连续失败 1. 查 cron-alerts.json 2. 查 STDERR 3. 常见原因:delivery.channel 未配置、脚本语法错误、timeout 不足
数据日期错乱 1. 查 state/*.jsondate 字段 2. 确认脚本走了 load_state() 日期检查
Gateway 无响应 pgrep -f "openclaw gateway" 确认进程存在,不要只看 exit code

更多排查细节见 references/troubleshooting.md


贡献指南

欢迎提交 Issue 和 PR。

提交 Issue 前请确认:

  1. 已查看 troubleshooting.md
  2. 已运行 get_summary()get_alerts() 获取任务状态
  3. 已查看 logs/{job_id}.log 获取详细错误信息

PR 规范:

  • 修改 SKILL.md 时需同步更新 references/ 中的对应文档
  • 新增功能需补充故障排查案例
  • 保持向后兼容,cron_logger.py 的 CLI 接口不要轻易变更

许可证

MIT License


相关项目


维护者:麻辣虾尾
最后更新:2026-05-30

About

OpenClaw cron task state management & logging framework

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages