Files
skill-template/development/LOGGING.md
chendelian a7baba7210
All checks were successful
技能自动化发布 / release (push) Successful in 7s
Codify Skill Action sync/async and task-center gold standards (v1.0.40)
2026-07-14 07:55:55 +08:00

313 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 日志规范
本文件是 skill 日志规范的**权威入口**,面向技术人员与 AI 编程代理。涉及长任务、RPA、外部系统对接的技能**必须**在实现前通读本文,并在 code review 时对照「必打日志节点清单」人工检查。
---
## 1. 为什么日志是必需的
OpenClaw 技能常在客户电脑上异步执行,且大量依赖 RPA、浏览器、ERP、微信等外部系统。这类任务具备以下特征
- **耗时长**:单次 run 可能数分钟甚至更久stdout 长时间无输出不代表卡死。
- **失败点多**网络抖动、页面改版、登录态失效、验证码、U 盾等都会导致中途失败。
- **环境差异大**:客户 OS、浏览器版本、代理、杀毒软件、屏幕分辨率各不相同无法复现开发机行为。
- **必须离线排查**:技术支持往往无法远程登录客户电脑,只能依赖本地落盘的日志与 artifacts。
日志用于定位:
| 维度 | 说明 |
|------|------|
| 失败阶段 | 任务停在鉴权、取号、打开页面、提交还是等待结果 |
| 输入范围 | `task_type``target_id``input_id``batch_id` |
| 外部系统状态 | HTTP 状态、RPA 步骤、兄弟技能返回码 |
| 耗时 | `elapsed_ms`、批量 `current/total` |
| 重试与人工介入 | 第几次重试、是否等待验证码 / U 盾 |
| 存证路径 | 截图、录屏、`video_log`、失败 artifacts |
**没有关键节点日志 = 客户现场不可排障。** 不要把 `print` 当作日志stdout 面向用户与 Agent文件日志与 Run Journal 面向运维。
---
## 2. 日志分层
技能日志分四层,职责互不替代:
### 2.1 统一文件日志(开发 / 运维排查)
**来源**`jiangchang_skill_core.unified_logging`(技能内通过 `util.logging_config` 薄封装 import
| API | 用途 |
|-----|------|
| `setup_skill_logging` | CLI 入口启动时初始化(`cli/app.py``main()` |
| `get_skill_logger` | 获取带 `skill_slug` 的 logger |
| `subprocess_env_with_trace` | 子进程 / 兄弟技能调用时传递 `trace_id` |
**路径**
```text
{JIANGCHANG_DATA_ROOT}/{JIANGCHANG_USER_ID}/logs/jiangchang.log
```
**特征**:每行带 `trace_id``skill_slug`;适合 grep、离线打包发给技术支持。
### 2.2 Activity / Run Journal宿主 UI 与用户可见进度)
**来源**`jiangchang_skill_core.activity`
| API | 用途 |
|-----|------|
| `emit` | 推送用户可读进度(不写 stdout进入步骤前自动步骤闸门SRCP |
| `step` | 结构化步骤 |
| `finish` | 任务结束;**唯一**写单行 result JSON 到 stdout 的场景 |
| `job_context` | 包裹 `cmd_run`;未捕获异常 / `JobStopped` 时自动 `finish` |
| `rpa_step` | RPA 专用步骤:闸门 + ▶/✓ emit |
| `interruptible_sleep` | RPA 等待(替代裸 `asyncio.sleep`),可暂停/停止 |
| `checkpoint` | 长循环内 consult control一般由 kit 自动调用) |
**路径**
```text
{JIANGCHANG_DATA_ROOT}/.jiangchang/runs/{job_id}.jsonl
```
**约定**
- `emit` **不写 stdout**;长任务应持续 emit避免用户以为卡死。
- `finish` 才输出单行 result JSON 供宿主解析。
- RPA 类技能:`RpaVideoSession.add_step` 会自动同步 activity无需重复手写每步 emit
- **禁止**自建 `scripts/util/progress.py` 或向 stdout 打印 `type:progress` JSON进度只走 Run Journal。
- 宿主通过 `{job_id}.control.json` 下发暂停/继续/停止;技能侧由 platform-kit 在步骤闸门自动处理(见 §2.5)。
### 2.5 步骤控制SRCP v1platform-kit >= 1.2.0
**权威行为定义在本文**platform-kit README 为实现细节参考。
| 通道 | 方向 | 载体 |
|------|------|------|
| 进度 | 技能 → 宿主 | Run Journal`emit` / `rpa_step` / `add_step` |
| 结果 | 技能 → 宿主 | `finish()` → Journal 终态 + stdout 一行 result |
| 控制 | 宿主 → 技能 | `{JIANGCHANG_DATA_ROOT}/.jiangchang/runs/{job_id}.control.json` |
`control.json``command` 只认:`none` | `pause` | `resume` | `stop`
技能作者三件事:
1. `cmd_run` 外包 `with job_context(skill=SKILL_SLUG):`
2. 关键步骤 `emit(...)``@rpa_step` / `video.add_step`
3. 每个出口 `finish(status=..., message=..., skill=SKILL_SLUG, **fields)` — **不要**手写多行 JSON 结果
**单内核多入口**`execute_single_*` 不含 `job_context`;仅 `cmd_run` / `cmd_run_pick` 包裹。批量 pick 循环内 `checkpoint()` + `emit(..., progress=...)`;部分失败用 `finish(status="partial")`。完整范式见 [`SKILL_ACTION_RUNTIME.md`](SKILL_ACTION_RUNTIME.md)。
RPA 等待用 `interruptible_sleep`**不要**裸 `asyncio.sleep`(否则暂停响应滞后)。
暂停在**步骤边界**生效(当前 `@rpa_step` / `emit` 执行完后、下一步开始前),与影刀「当前指令完成后暂停」一致。
Journal 会写入 `type: lifecycle` 事件(`paused` / `resumed` / `stopping`),供宿主任务中心显示状态。
### 2.3 `task_logs`(任务结果审计)
**来源**`db/task_logs_repository.save_task_log`
- 适合 `python scripts/main.py logs` / `log-get <id>` 查询。
- 记录任务最终状态、`result_summary`(可含 `video_path``video_log` 等)。
- 面向「这次任务成功还是失败、摘要是什么」,不是逐步 trace。
- **鉴权失败与未捕获异常也必须写入 `task_logs`**,保证 `logs` / `log-get` 排查链不断(见模板 `cmd_run` 示范)。
表结构与字段见 [`references/SCHEMA.md`](../references/SCHEMA.md)。
### 2.4 RPA video step / artifacts用户可见动作与存证
**来源**`RpaVideoSession``rpa-artifacts/` 目录
- video artifact 是**标准可启用能力****不保证默认每次生成 MP4**(模板默认 `OPENCLAW_RECORD_VIDEO=0`)。
- **`OPENCLAW_RECORD_VIDEO=0`**:不生成最终录屏,但 RPA / 长任务仍须保留 `RpaVideoSession``video.add_step` 与 Activity 进度同步。
- **`OPENCLAW_RECORD_VIDEO=1`**`result_summary` 应包含 `video_path``raw_video``video_log``video_warnings` 等字段CLI 可打印录屏路径与诊断。
- 用户可见的中文步骤(「打开登录页」「点击提交」)。
- 失败截图:`{数据目录}/rpa-artifacts/{batch_id}/...`(受 `OPENCLAW_ARTIFACTS_ON_FAILURE` 控制,默认开)
详见 [`RPA.md`](RPA.md) §5 存证与录屏规范。
---
## 3. 必打日志节点清单
以下节点应在**生产代码**中有对应 `log.info` / `log.warning` / `log.exception`(或 Activity / RPA step。复制模板后按业务增删但**不可整体省略**。
| 阶段 | 建议日志 / emit |
|------|-----------------|
| CLI 入口启动 | `cli_start``cli/app.py`,已由模板提供) |
| 参数解析完成 | `run_args target_id=... input_id=...` |
| 鉴权开始 | `entitlement_check_start` |
| 鉴权成功 | `entitlement_ok` |
| 鉴权失败 | `entitlement_failed``warning` |
| 任务开始 | `task_start task_type=... target_id=... input_id=... batch_id=...` + `emit("开始处理任务")` |
| adapter / test target 选择 | `adapter_selected adapter=... system=...` |
| 账号 / profile / lease 获取 | `account_acquired account_id=...` |
| 账号 / lease 释放 | `account_released account_id=...` |
| 外部 API 开始 | `external_call_start system=... operation=...` |
| 外部 API 结束 | `external_call_done system=... operation=... elapsed_ms=... status=...` |
| 兄弟技能调用开始 / 结束 | 同上,或 `sibling_call_start/done skill_slug=...` |
| RPA 操作开始 / 结束 | `rpa_start` / `rpa_done` + `video.add_step` 中文步骤 |
| 打开页面 | step「打开 xxx 页面」 |
| 检查登录 | step + log |
| 定位输入框 / 点击提交 | step + log |
| 等待结果 | step + log含 timeout 预期) |
| 批量循环 | `batch_progress current=... total=...` |
| 重试 | `retry_attempt n=... reason=...``warning` |
| 跳过 / 部分失败 | `skipped` / `partial_failure``warning` |
| 人工介入 | `human_intervention type=captcha|login|ukey|otp``warning` |
| 失败截图 / 视频 | `artifact_saved artifact_path=...` / `video_path=...` |
| 任务成功 | `task_success ...` + `task_log_saved status=success` |
| 任务失败 | `task_failed ...``exception` 保留 traceback+ `task_log_saved status=failed` |
---
## 4. 字段规范
推荐在 `log.info` 等消息中使用 **key=value** 结构化字段,便于 grep
| 字段 | 含义 |
|------|------|
| `task_type` | 任务类型(与 `task_logs.task_type` 对齐) |
| `target_id` | 目标标识(平台、店铺、报表类型等) |
| `input_id` | 输入记录 ID |
| `batch_id` | 批次 / 录屏批次 |
| `stage` | 阶段:`auth` / `run` / `cleanup` |
| `system` | 外部系统名:`erp` / `wechat` / `1688` |
| `operation` | 操作名:`submit_order` / `fetch_report` |
| `adapter` | adapter 档位:`mock` / `simulator_api` / `real_api` |
| `elapsed_ms` | 耗时毫秒 |
| `status` | `ok` / `failed` / `timeout` / `skipped` |
| `error_code` | 业务错误码(非 Python 异常名) |
| `artifact_path` | 截图 / 附件路径 |
| `video_path` | 录屏成品路径 |
示例:
```text
external_call_done system=erp operation=download_report elapsed_ms=4523 status=ok
batch_progress current=7 total=20 target_id=store-001
```
---
## 5. 日志级别规范
| 级别 | 使用场景 |
|------|----------|
| **info** | 关键阶段、成功路径、外部调用开始/结束、批量进度、`task_log_saved` |
| **warning** | 可恢复问题、重试、跳过、人工介入、部分失败、鉴权失败(业务可预期) |
| **exception** | 异常路径;**必须**用 `log.exception(...)` 保留 traceback |
| **debug** | 低频诊断;**默认不依赖 debug 才能排查问题** |
原则:**生产排障应主要靠 info + warning + exception**,不要把 debug 当作唯一线索。
---
## 6. 敏感信息红线
### 禁止记录原始值
以下字段**不得**以明文出现在日志、Activity、task_logs 的 `error_msg` / `result_summary`、stdout
- `password``token``cookie``secret``api_key``authorization`
- 验证码、动态口令全文
- 身份证、银行卡、手机号**完整值**
### 建议记录的安全替代
| 替代字段 | 示例 |
|----------|------|
| `credential_ref` | vault / account-manager 引用 ID |
| `account_id` | 账号表主键 |
| `masked_user` | `138****8000` |
| `domain` | `erp.example.com` |
| `last4` | 卡号后四位 |
| `hash` / `short_id` | 内容摘要 |
变量名可以叫 `token`,但日志里不能出现 `token=eyJhbG...` 或 f-string 拼接明文。
---
## 7. 推荐代码示例service 层)
```python
from util.logging_config import get_skill_logger
from jiangchang_skill_core.activity import emit, finish, job_context, rpa_step, interruptible_sleep
from util.constants import SKILL_SLUG
log = get_skill_logger()
def cmd_run(target=None, input_id=None):
task_type = "your_task"
with job_context(skill=SKILL_SLUG):
log.info(
"task_start task_type=%s target_id=%s input_id=%s",
task_type, target, input_id,
)
emit("开始处理任务", skill=SKILL_SLUG, stage="run")
try:
ok, reason = check_entitlement(SKILL_SLUG)
if not ok:
log.warning("entitlement_failed task_type=%s reason=%s", task_type, reason)
finish(status="failed", message=reason, skill=SKILL_SLUG, error_code="ENTITLEMENT_DENIED")
return 1
t0 = time.monotonic()
# ... 外部调用 / RPA@rpa_step 或 video.add_step等待用 interruptible_sleep ...
elapsed_ms = int((time.monotonic() - t0) * 1000)
log.info(
"external_call_done system=%s operation=%s elapsed_ms=%d status=%s",
"erp", "submit", elapsed_ms, "ok",
)
tlr.save_task_log(..., status="success", ...)
log.info("task_log_saved task_type=%s status=success", task_type)
finish(status="success", message="任务完成", skill=SKILL_SLUG)
return 0
except Exception:
log.exception(
"task_failed task_type=%s target_id=%s input_id=%s",
task_type, target, input_id,
)
emit("任务失败,已记录诊断信息", type="warn", skill=SKILL_SLUG, stage="run")
tlr.save_task_log(..., status="failed", error_msg="...", ...)
finish(status="failed", message="任务执行异常", skill=SKILL_SLUG)
return 1
```
模板示范见 [`scripts/service/task_service.py`](../scripts/service/task_service.py) 的 `cmd_run`
---
## 8. 错误排查顺序
客户现场或测试机排障,建议按以下顺序(由快到慢、由用户可见到运维细节):
1. **CLI stdout** — 是否有 `ERROR:<CODE>:``HINT:`、模板提示文案。
2. **`python scripts/main.py logs`** — 最近任务是否写入 `task_logs`、status 与 `error_msg`
3. **`python scripts/main.py log-get <id>`** — 单条 JSON`result_summary`video 路径等)。
4. **统一日志文件**`{JIANGCHANG_DATA_ROOT}/{JIANGCHANG_USER_ID}/logs/jiangchang.log`grep `trace_id``task_failed`)。
5. **Run Journal**`{JIANGCHANG_DATA_ROOT}/.jiangchang/runs/{job_id}.jsonl`(宿主 UI 进度来源)。
6. **RPA artifacts**`rpa-artifacts/` 失败截图;若 `OPENCLAW_RECORD_VIDEO=1` 还有录屏 MP4、`video_log`。未开启录屏时可能只有截图或日志,需要完整录屏请在用户 `.env` 中设置 `OPENCLAW_RECORD_VIDEO=1`
长时间无 stdout **不代表卡死**;先看 Run Journal / Activity再看统一日志。
---
## 9. 与现有文档关系
| 文档 | 关系 |
|------|------|
| [`RUNTIME.md`](RUNTIME.md) | 统一日志能力来源、`util.logging_config` 薄封装、数据根约定 |
| [`TESTING.md`](TESTING.md) | 日志相关 hard policy 测试(`test_development_policy_guard.py` |
| [`RPA.md`](RPA.md) | RPA step、`RpaVideoSession`、video / artifacts 标准 |
| [`SCHEMA.md`](../references/SCHEMA.md) | `task_logs` 表结构与查询命令 |
| [`CLI.md`](../references/CLI.md) | `logs` / `log-get` 命令与手工排查入口 |
| [`CONFIG.md`](CONFIG.md) | 敏感配置不进日志;`.env` 边界 |
| [`DEVELOPMENT.md`](DEVELOPMENT.md) | `service/` 层日志职责与发布前检查清单 |
| [`SKILL_ACTION_RUNTIME.md`](SKILL_ACTION_RUNTIME.md) | 任务中心 / async Job 与 `job_context` 边界 |
| [`POLICY_MATRIX.md`](POLICY_MATRIX.md) | 可自动检测的 logging hard policy 索引 |