调用追踪、页面留档与文件上传
本文说明服务在自己的工作目录里留下了什么、放在哪、以及客户端-服务器模式下怎么把文件送到服务端。这些产物是排障与事后复盘的唯一凭据,同时也是需要定期清理的磁盘占用。
一、每一次调用都有留档
每一次调用(含批量里的每一步)的请求与响应都会落到 <启动目录>/logs/trace/<自然日>/ 下,例如 logs/trace/20260924/:
| 文件 | 内容 | 用途 |
|---|---|---|
steps.log | 每次调用一行:时间、序号、任务 ID、方法、成功与否、耗时、一句话结果 | 人看的时间线,一眼看出第几步开始不对 |
calls.jsonl | 每次调用一行 JSON:调用摘要 + 从 data 里摘出的关键字段(url / title / seq / screenshot / state_file / changed 等)+ 完整请求体 | 机器读,按 id 或 method 过滤 |
NNNNNN-<任务ID>-<方法名>.json | 这一次调用的完整请求与完整响应(含整页 data.text) | 要看原始报文、要复现某一步时的唯一凭据 |
uploads.log | POST /playwright/upload 的落盘记录:文件名、大小、摘要 | 只记元数据,不记文件内容 |
文件名形如 000001-1001-get_browser_state.json,序号与页面留档的序号可以互相印证。
排查顺序建议:先看 steps.log 定位出问题的那一步 → 再看同名序号的 .json 看完整报文 → 最后按里面的 data/<id>/<序号>.txt 与 .png 看当时页面。
写盘失败(磁盘满、目录没权限)只留一条警告,不会让浏览器命令失败 —— 所以不能把日志存在当成命令成功的证据,反过来命令失败也不代表日志写失败。
脱敏
browser.trace.redact.enabled 默认开启,落盘前会把以下内容替换成掩码(默认 ***):
- 手机号;
- 18 位身份证号 / 统一社会信用代码;
- 邮箱;
- 16~19 位长数字。
browser.trace.redact 可以追加自定义正则(逗号分隔),例如把公司名、商标名也掩掉;browser.trace.redact.mask 改掩码文本。
脱敏是「尽力而为」:按模式匹配,认不出来的个人信息(姓名、门牌号、账号)不会被掩掉。填表类任务里请求体带着姓名、手机号、证件号、详细地址是常态,交付或共享
logs/trace/之前请自己过一眼。需要原文时把browser.trace.redact.enabled设为false。
其他开关
上述 trace 脱敏不代表 data/<id>/ 的截图、页面文本或临时参数文件也已经脱敏。六位短信验证码、密钥、签名链接和图片中的个人信息尤其不能依赖普通模式匹配识别;分享前应逐类核验。操作经验见表单与多层弹窗排障。
| 配置项 | 默认 | 说明 |
|---|---|---|
browser.trace.enabled | true | 关掉之后完全不落盘 |
browser.trace.dir | <启动目录>/logs/trace | 换目录 |
browser.trace.maxRecordChars | 8000000 | 单次调用完整报文的字符上限,超了截断并打标记。只有 screenshot 不带 path 时的内联大图可能撞到 |
二、页面留档:data/<id>/
页面变化会自动留档,落在进程工作目录下的 data/<任务ID>/ 里:
| 时机 | 产物 |
|---|---|
| 「会改变页面」的方法执行成功 | data/<id>/<序号>.png 一张截图 |
每次 get_browser_state | data/<id>/<序号>.png 截图 + 同名 .txt |
显式调 screenshot / get_element_screenshot | data/<id>/shot-N.png |
几点要知道:
- 序号是每个任务独立的自增序号,从 1 开始;一对
.png/.txt序号相同,表示是同一时刻的页面。 - 两个文件都可以直接 GET:
GET <服务地址>/data/<id>/<序号>.png与.../<序号>.txt。 <序号>.txt的内容是「页签文本块 + 空行 + 可交互结构化文本」,也就是data.browser_state加data.text,方便事后离线复看某一步的页面。- 哪些方法算「会改变页面」:导航类、点击与交互类、滚动与鼠标类、页签类、等待类、
execute_js与部分设置类。纯读取类(get_url、get_cookies、is_visible等)不截图,否则每读一个值就多一张一模一样的图。 - 批量调用时每一步的结果里都有它自己那一步的截图,所以一次批量请求就能拿到整段操作的页面变化历史。
- 截图前会尽力等页面进入
DOMCONTENTLOADED(有上限,等不到也照常截图),不会因为等待失败丢掉这一张。 - 截图失败不会让方法失败,原因在
data.screenshot_error。 - 长时间跑下来
data/会涨得很快,browser.capture.enabled=false可以只关掉自动留档(显式调screenshot不受影响)。
这些文件不会自动清理,data/ 已经加进版本控制的忽略列表;不需要时直接删目录,或用 cleanup(默认只预演,要真删必须显式传 dryRun:false)。
三、客户端侧的留档
服务端记的是「收到了什么」,客户端记的是「发出去了什么」—— 两者在「服务没起来、请求根本没发出去」这种情况下只有客户端那份能说明问题。
| PowerShell 脚本 | Python 客户端 | |
|---|---|---|
| 形态 | 传一个请求文件发一次请求 | 子命令式 CLI,也可作为库导入 |
| 留档位置 | logs/agent/<会话>/ | 同左 |
| 文件 | NNN.req.json / NNN.res.json 成对 + 一份 steps.log | 同左 |
请求在发送前就落盘,所以连「没发出去」也能看出来。客户端侧使用与服务端同一套脱敏规则。
四、文件上传:POST /playwright/upload
客户端-服务器模式下,upload_file 的 path 必须是服务端能打开的路径。所以客户端要先把文件送到服务端,再把返回的路径回填给 upload_file。
同一个路由支持四种用法:
| 用法 | 说明 |
|---|---|
POST /playwright/upload?filename=图样.jpg | 请求体直接是文件字节 |
POST /playwright/upload(multipart/form-data) | 文件字段默认叫 file |
POST /playwright/upload(application/json) | {"filename":"图样.jpg","contentBase64":"..."} |
GET /playwright/upload | 列出暂存目录里的文件 |
DELETE /playwright/upload?name=图样.jpg | 删除一个文件 |
回执里的 path(服务端绝对路径)与 relativePath(相对暂存目录的名字)都可以直接喂给 upload_file。
安全约束:
- 文件名会被清洗(只留基本名、去掉路径分隔符与控制字符、保留中文),并且只能落在暂存目录里;
- 单文件上限由
browser.upload.maxBytes控制(默认 64MB,0表示不限); - 同名文件默认覆盖,
browser.upload.overwrite=false时自动改名(a.jpg→a-1.jpg); - 整个接口可以用
browser.upload.enabled=false关掉 —— 对外暴露的服务建议关掉它。
暂存目录默认是 <启动目录>/upload,同样不会自动清理。
五、execute_js 的脚本目录
execute_js 支持把脚本放在服务端,用 bodyFile 引用:
{"id":"1001","method":"execute_js","params":{"bodyFile":"read-table.js","vars":{"sel":".vxe-table--body-wrapper"}}}
bodyFile只能读脚本目录下的文件(默认<启动目录>/scripts/js,可用browser.js.dir改),防止用它去读任意文件;- 脚本里可以用
{{变量名}}占位,由vars注入 —— 中文、引号、换行都不用在客户端拼字符串。
这条路径存在的意义是绕开传输层的坑:多行脚本经命令行或消息队列传递时可能被截断,到服务端只剩第一行,报的还是语法错误(Unexpected end of input),很难联想到是传输问题。服务在检测到「脚本像是被截断了」时会直接给出改用 bodyFile 的提示。
