使用 Playwright 编写 OneUptime 合成监控脚本:从模拟用户交互到自定义指标采集
【免费下载链接】oneuptimeComplete open-source monitoring and observability platform.项目地址: https://gitcode.com/GitHub_Trending/on/oneuptime
合成监控(Synthetic Monitoring)是一种通过模拟真实用户交互来主动探测应用可用性与性能的监控方式。本指南以 OneUptime 的合成监控(Synthetic Monitor)为核心,讲解如何使用内置的 Playwright 兼容page对象编写浏览器交互脚本、通过screenshots侧通道保留失败现场证据、利用monitorSecrets安全注入敏感信息,并通过oneuptime.captureMetric()输出自定义指标用于 Metric Explorer 可视化与告警。读完本文,你将能够编写一份可运行、可调试、可复用的合成监控脚本。
合成监控是什么
OneUptime 的合成监控通过模拟用户交互(点击按钮、填写表单、页面跳转、截图取证等)来主动验证应用在全球不同地理位置的可用性与性能,而不是被动等待真实用户报告故障。你只需在监控脚本中描述"一个用户会怎么做",探针(Probe)就会代表你在目标站点上重复执行这些行为。
与简单的 HTTP 探测不同,合成监控运行在一个真实的浏览器上下文中,因此可以覆盖以下场景:
- 登录流程、购物车结算、多步表单等复杂交互链路的可用性;
- 前端页面渲染速度、关键元素出现时间等性能指标;
- 从多个地理位置、多种浏览器内核与屏幕尺寸组合下的一致性验证。
从源码结构看,OneUptime 的合成监控由探针侧的 SyntheticRuntime 运行环境 完整支撑,它包含 SyntheticMonitorWorker.ts(浏览器 Worker 入口)、WorkerController.ts(RPC 能力控制器)、PlaywrightCapabilityBroker.ts(Playwright 能力代理)与 WorkerBootstrap.ts(沙箱引导代码)等模块,负责将用户脚本安全地映射到真实浏览器操作上。
快速开始:一个最小可用脚本
在 OneUptime Dashboard 中创建合成监控后,你会获得一个脚本编辑入口。以下示例完整展示了脚本上下文(script context)中可用的对象,以及最基本的"打开页面 → 截图 → 返回数据"流程:
// Objects available in the context of the script are: // - axios: Axios module to make HTTP requests // - page: OneUptime's secure Playwright-compatible page facade // - browserType: Browser type in the current run context - Chromium or Firefox // - screenSizeType: Screen size type in the current run context - Mobile, Tablet, Desktop // You can use these objects to interact with the browser and make HTTP requests. await page.goto("https://playwright.dev/"); // The commonly used Page, Locator, Frame, and BrowserContext APIs are supported. // Here are some of the variables that you can use in the context of the monitored object: console.log(browserType); // This will list the browser type in the current run context - Chromium or Firefox console.log(screenSizeType); // This will list the screen size type in the current run context - Mobile, Tablet, Desktop // Playwright page object belongs to that specific browser context, so you can use it to interact with the browser. // To take screenshots, assign them to the `screenshots` object that is provided // in the script context. Screenshots captured this way are preserved even if the // script later throws — useful for debugging failed runs. screenshots["screenshot-name"] = await page.screenshot(); // you can save multiple screenshots and have them with different names. // when you want to return a value, use return statement with data as a prop. // To log data, use console.log // console.log('Hello World'); // You can access the browser context via page.context() if needed (for example, to create a new page or dealing with popups). return { data: "Hello World", };脚本要点:
page.goto()负责导航,它是所有浏览器交互的起点;console.log()输出的内容会出现在监控运行日志中,是脚本调试的第一手段;return { data: ... }是脚本向监控系统返回结果的标准方式,返回值会被序列化为 JSON 存储;browserType与screenSizeType是当前运行上下文的内置变量,可用来区分不同浏览器/屏幕组合下的行为差异。
返回值序列化规则
脚本返回的数据在存储前会经过 JSON 序列化,行为与JSON.stringify一致:
- 普通对象与数组中的
NaN、Infinity会被转换为null; undefined属性与函数会被丢弃;Date对象会变成 ISO 字符串;- 类实例等非纯对象会被整体丢弃。
因此建议只返回可 JSON 化的纯数据,例如return { data: { loadTime } }。
深入理解 Playwright 的使用边界
OneUptime 使用 Playwright 模拟用户交互,但脚本并不直接运行在探针的 Node.js 进程里。page是一个经过安全封装的、与 Playwright 兼容的门面对象(facade),常用的Page、Locator、Frame、ElementHandle、JSHandle、Request、Response、keyboard、mouse 以及 browser-context 方法均可使用,涵盖导航、定位器、点击、表单输入、页面求值、弹窗、多页面、响应检查与截图等能力。
这一点在 WorkerBootstrap.ts 的源码注释中有明确印证:用户脚本运行在独立的浏览器 Web Worker 中,所有可见对象都是 Worker 域内包含拷贝数据或不透明标识的代理,数据跨越运行时边界时以拷贝或"执行级能力句柄"的形式传递。
哪些 API 不可用
出于安全隔离考虑,会逃逸隔离边界的能力被有意禁用,调用时会抛出带引导信息的明确错误:
| 类别 | 不可用 API | 替代方案 |
|---|---|---|
| 浏览器启动/连接 | browserType.launch()、connect()、connectOverCDP()、launchPersistentContext() | 无需自行启动,脚本内直接使用page |
| CDP 会话 | newCDPSession() | 不使用 |
| 请求路由 | page.route()、routeFromHAR()、page.request.* | 使用axios全局对象发起 HTTP 请求 |
| 事件监听器 | page.on(...)、page.once(...)等 | 使用page.waitForEvent(...)、字符串/正则匹配的请求与响应等待 |
| 同步 frame 访问器 | page.frames()、page.mainFrame()、page.frame(...) | 使用page.frameLocator(...)处理 iframe |
| 私有字段/宿主路径 | Playwright 私有字段、读写宿主文件系统路径的选项 | 不使用 |
| 浏览器权限 | 剪贴板、摄像头、麦克风、MIDI、本地字体等 | 仅 geolocation 与 notifications 权限可用 |
| 输出形式 | 整页截图、PDF 输出 | 使用视口(viewport)截图 |
| 浏览器上下文 | page.context().browser() | 不可用 |
几点重要细节:
- 每执行最多 8 个页面(源码常量
MAX_CONTEXT_PAGES = 8); - 事件、请求、响应、URL 等待方法的函数谓词无法跨越隔离边界,请改用字符串或正则匹配器、定位器或显式轮询;
- 传给
page.evaluate()等方法的求值函数,会在被监控的浏览器页面内执行,绝不会在探针进程中执行; page.waitForNavigation(...)、page.setDefaultTimeout(...)、page.setDefaultNavigationTimeout(...)是受支持的。
以 WorkerBootstrap.ts 为例,源码中明确列出了被屏蔽的全局能力(BroadcastChannel、EventSource、SharedWorker、WebSocket、Worker、XMLHttpRequest、fetch、importScripts等),并把launch、connect、route、tracing等属性列入blockedProperties——脚本内的所有 HTTP 通信都应经由axios或http/https门面完成,浏览器网络能力由探针统一代理。
截图取证:screenshots 侧通道
脚本上下文中预声明了一个screenshots对象。你可以在脚本任意位置将截图赋值给它——即使脚本随后抛出异常(包括断言失败、超时或意外错误),这些截图依然会被保留,从而精确还原运行失败时页面所处的状态。成功捕获的截图会出现在 OneUptime Dashboard 中该次监控运行的详情里。
// Capture screenshots via the `screenshots` side-channel — they are preserved on both success and failure. await page.goto("https://app.example.com/login"); screenshots["login-page"] = await page.screenshot(); await page.fill("#email", "user@example.com"); await page.fill("#password", "wrong"); await page.click("button[type=submit]"); // If the next assertion throws, the `login-page` screenshot above is still captured. await page.waitForSelector(".dashboard", { timeout: 5000 }); screenshots["dashboard"] = await page.screenshot(); return { data: "Login succeeded", };示例中,即使waitForSelector(".dashboard", { timeout: 5000 })断言失败,login-page这张"失败前一刻"的截图仍然会被保存,这是排查登录问题的最直接证据。
旧式截图返回方式(Legacy)
出于向后兼容,脚本也可以把截图作为返回值的一部分返回。但这种方式只有在脚本正常结束时才会捕获截图——一旦脚本抛出异常,截图就会丢失。
// Legacy pattern — screenshots only captured on successful return. const screenshots = {}; screenshots["screenshot-name"] = await page.screenshot(); return { data: "Hello World", screenshots: screenshots, };需要失败现场证据时,请优先使用上面的侧通道(side-channel)模式,而不是旧式返回模式。
使用 Monitor Secrets 注入敏感信息
监控脚本往往需要访问 API Key、密码等敏感信息。Monitor Secrets 提供加密存储与按监控授权的注入能力,避免把凭据硬编码进脚本。
添加 Secret
创建路径:OneUptime Dashboard → Monitors → Settings → Secrets → Create Monitor Secret。
创建时可以指定哪些监控(monitor)有权访问该 Secret。需要注意:
- Secret 加密存储;
- Secret 保存后无法再次查看或修改其值,丢失后只能创建新 Secret;
- 需要轮换时,可点击该行上的Update Secret Value按钮更新值,无需删除重建。
在脚本中引用 Secret
在脚本中通过monitorSecrets对象引用已授权的 Secret,语法为模板占位符形式:
// if your secret is of type string then you need to wrap it in quotes let stringSecret = '{{monitorSecrets.StringSecret}}'; // if your secret is of type number or boolean then you can use it directly let numberSecret = {{monitorSecrets.NumberSecret}}; // if your secret is of type boolean then you can use it directly let booleanSecret = {{monitorSecrets.BooleanSecret}}; // you can even console log to see if the secrets is being fetched correctly console.log(stringSecret);类型规则:
- 字符串(string)类型的 Secret 必须用引号包裹:
'{{monitorSecrets.StringSecret}}'; - 数字(number)或布尔(boolean)类型的 Secret 可直接使用,无需引号:
{{monitorSecrets.NumberSecret}}。
从 monitor-secrets 文档 可以确认:Secret 是在探针上、在合成监控/自定义代码监控脚本执行之前被注入的,因此脚本运行时{{monitorSecrets.ApiKey}}这样的引用已经解析为解密后的真实值。Secret 同样可用于 API 监控的请求头/请求体/URL、Website/IP/Port/Ping/SSL 监控的 URL,以及 SNMP 监控的 community string 与 SNMPv3 认证密钥中。
采集自定义指标:oneuptime.captureMetric()
脚本可以通过oneuptime.captureMetric(name, value, attributes)输出自定义指标,这些指标被存储在 OneUptime 中,可在Metric Explorer中绘制成图表,用于告警与筛选。
oneuptime.captureMetric(name, value, attributes);参数说明:
name(string,必填):指标名称,例如"dashboard.load.time"。存储时会自动加上custom.monitor.前缀;value(number,必填):数值型指标值;attributes(object,可选):附加上下文的键值对。
实战示例:采集页面加载耗时
await page.goto("https://app.example.com"); const startTime = Date.now(); await page.waitForSelector("#dashboard-loaded"); const loadTime = Date.now() - startTime; // Capture page load time as a custom metric oneuptime.captureMetric("dashboard.load.time", loadTime, { page: "dashboard", }); screenshots["dashboard"] = await page.screenshot(); return { data: { loadTime }, };采集后,指标会以custom.monitor.dashboard.load.time之类的名称出现在 Metric Explorer 中。你可以把它加入 Dashboard 图表、设置告警,并按监控、探针、浏览器类型、屏幕尺寸或自定义属性进行筛选。
指标限制
- 单次脚本执行最多采集100条指标(源码常量
MAX_METRICS = 100); - 指标名称最长200字符;
- 值必须是数字(在 WorkerBootstrap.ts 的
oneuptime.captureMetric实现中,非有限数字会直接忽略)。
从 WorkerBootstrap.ts 的实现可以看到,每条指标最多携带 50 个属性,属性键截断为 200 字符、属性值转为字符串并截断为 1000 字符;指标通过 RPC 消息以captureMetric方法回传给探针侧的 PlaywrightCapabilityBroker.ts(其中"captureMetric"位于该 Broker 支持的方法白名单中),最终进入监控运行结果。
脚本内可用的模块一览
| 模块 | 说明 |
|---|---|
page | 与浏览器交互的安全 Playwright 兼容门面。可通过page.context()访问执行上下文以创建页面或处理弹窗;浏览器启动/连接、CDP、路由、绑定、私有字段与宿主路径选项不可用 |
screenshots | 预声明的对象,向其赋值截图(如screenshots['login-page'] = await page.screenshot()),即使脚本后续抛出异常也会被保留 |
axios | 基于 Promise 的 HTTP 客户端,支持可调用式 Axios 及request、get、head、options、post、put、patch、delete、create。请求/响应大小、重定向与超时有限制;自定义 transport、adapter、socket、agent 与 proxy 覆盖不可用 |
crypto | 浏览器 Worker 实现的 SHA-256 哈希、HMAC-SHA-256、randomBytes、randomInt、randomUUID |
console.log | 输出日志到控制台,用于调试,日志会出现在监控运行的日志区 |
oneuptime.captureMetric | 从脚本采集自定义指标,见上文"自定义指标"章节 |
http | 仅客户端的缓冲式兼容门面,支持request、get、Agent |
https | 与http门面对应的 HTTPS 版本 |
从源码角度看,crypto门面在 WorkerBootstrap.ts 中被刻意收窄为仅 SHA-256/HMAC-SHA-256/随机数能力(createHash、createHmac、randomBytes、randomInt、randomUUID),其他算法会明确抛错;http/https是建立在axios之上的、仅客户端、缓冲式的事件门面。这也再次印证了合成监控"能力最小化、隔离最大化"的安全设计。
需要特别注意的点
page对象是浏览器交互的主要接口,它刻意实现的是Playwright 功能的 allowlist(白名单),而非直接暴露裸的 Playwright 或 Node.js 对象;- 使用
console.log记录日志,日志会出现在该监控的日志区; - 使用
return语句返回数据;截图请赋值给预声明的screenshots对象,以便脚本抛错时仍被保留; - 使用
browserType与screenSizeType变量获取当前运行上下文的浏览器类型(Chromium 或 Firefox)与屏幕尺寸(Mobile、Tablet、Desktop),可自由在脚本中使用; - 这是 JavaScript 脚本,你可以使用所有 JavaScript 语言特性;
- 使用
axios模块在脚本中发起 HTTP 请求、调用 API; - 使用 oneuptime.com 托管服务时,脚本上下文总是包含最新版本的 Playwright 与浏览器;自托管时,请确保探针更新到最新的 Playwright 与浏览器版本;
- 脚本默认超时为60 秒,可由探针运维者配置;超时的 Worker 及其所有浏览器子进程都会被终止(探针侧的 Limits.ts 同时定义了
MAX_SYNTHETIC_MONITOR_SCRIPT_TIMEOUT_IN_MS这一脚本超时上限); - 每次执行都有内存与可写浏览器存储的额度限制,超出任一额度都会终止该次执行并清理其临时 profile,自托管运维者可以在探针上配置这些上限。
小结
OneUptime 合成监控把"真实用户路径"转化为可重复、可度量、可留证的程序化脚本:用page驱动浏览器完成交互,用screenshots侧通道保留成功与失败两种状态下的页面证据,用monitorSecrets安全注入凭据,再用oneuptime.captureMetric()把业务关键耗时变成可查询、可告警的指标。配合全球多探针位置与浏览器/屏幕尺寸矩阵,它适合作为应用发布后的主动健康检查与性能回归手段。更多相关能力可继续阅读 Monitor Secrets 文档,或在 探针合成运行时源码 中深入其隔离与限流实现。
【免费下载链接】oneuptimeComplete open-source monitoring and observability platform.项目地址: https://gitcode.com/GitHub_Trending/on/oneuptime
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考