生产级 OpenClaw 定时任务状态管理与日志监控框架
在 OpenClaw 生态中,cron 定时任务是最常用的自动化手段——每日推送、状态追踪、定时提醒。但生产运行中总会遇到这些问题:
- 任务执行了,但消息没收到——不知道是脚本崩了还是通道断了
- 数据状态错乱——主 session 正在写 state,Worker 同时在读,JSON 损坏
- 连续失败没人知道——连续 16 次失败后才发现,已经晚了
- 排查靠猜——没有日志、没有状态聚合、没有告警
Cron State Manager 就是为解决这个问题而生:一个可直接复制使用的生产框架,覆盖状态管理、并发安全、统一日志、故障排查全链路。
| 目标 | 实现方式 |
|---|---|
| 可追溯 | 每次执行记录完整 stdout/stderr/delivery |
| 可监控 | cron-summary.json 聚合所有任务状态 |
| 可告警 | 连续失败 ≥3 次自动标记 |
| 可追踪 | message() 发送状态单独记录 |
| 高可用 | 原子读写 + 失败降级,不崩主流程 |
# 复制脚本
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每个 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()调用可能因通道延迟而超时)
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)1. 运行脚本获取内容
2. 发送结果到目标通道
3. 拿到 message() 返回的 messageId 后,运行:
python3 scripts/cron_logger.py delivery <job_id> <messageId> [status]
# 查看所有任务状态
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>.logfrom 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/*.json 的 date 字段 2. 确认脚本走了 load_state() 日期检查 |
| Gateway 无响应 | pgrep -f "openclaw gateway" 确认进程存在,不要只看 exit code |
更多排查细节见 references/troubleshooting.md。
欢迎提交 Issue 和 PR。
提交 Issue 前请确认:
- 已查看 troubleshooting.md
- 已运行
get_summary()和get_alerts()获取任务状态 - 已查看
logs/{job_id}.log获取详细错误信息
PR 规范:
- 修改 SKILL.md 时需同步更新 references/ 中的对应文档
- 新增功能需补充故障排查案例
- 保持向后兼容,
cron_logger.py的 CLI 接口不要轻易变更
MIT License
- OpenClaw — 本框架的运行平台
- hydration-reminder-agentic — 基于本框架的喝水提醒应用示例
维护者:麻辣虾尾
最后更新:2026-05-30