Cloudflare工人(工人)的默特化后端记录器
当地开发期间录制Cloudflare工人的后端会话的在职者shim(wrangler dev页:1 已部署 工人或网页项目。 它报告工人的入境请求,离开 fetch 调用(包括请求/答复机构,封顶为256 KB),绑定和 KV 调用和postgres.js 查询作为捕捉事件到一个Meticulous recorder sidecar,它将它们变成后端会话录音.
Shim是被动的:向外请求直接前往其真实目的地;侧车从不在请求路径上,无法到达的侧车或任何抓取失败都不会影响你的应用.
侧车住的地方是两个设置的唯一区别:
| 侧车 | 配置由 | |
|---|---|---|
wrangler dev | 地方进程npx @alwaysmeticulous/cli record backend(中文(简体) ) | 页:1 METICULOUS_SIDECAR_URL var. (中文(简体) ) |
已部署(wrangler deploy, (中文(简体) ), wrangler pages deploy,预览) | @alwaysmeticulous/backend-recorder-sidecar-worker,部署在您的 Cloudflare 账户 | (单位:千美元) METICULOUS_SIDECAR 服务绑定 |
地方发展的设置
-
安装 shim :
npm install @alwaysmeticulous/backend-recorder-workerd -
包装您的 ES 模块操作器 :
import { withMeticulous } from "@alwaysmeticulous/backend-recorder-workerd"; export default withMeticulous({ async fetch(request, env, ctx) { // your app }, }); -
启用
nodejs_als(或nodejs_compat) 相容性旗帜wrangler.toml数字 :compatibility_flags = ["nodejs_als"] -
通过 Meticulous CLI 运行您的 dev 命令, 启动记录器侧车并将其 URL 传递到
wrangler dev自动 :npx @alwaysmeticulous/cli record backend -- npx wrangler dev如果记录器不能启动——没有证书,没有网络来取取侧车捆,一个无法使用的端口——CLI会警告和运行你的dev命令没有记录而不是失败,所以这可以安全地离开
dev剧本.或者单独运行侧车
npx @alwaysmeticulous/cli record backend并将其指向自己——值必须是工人的var/约束(在工人内部看不到主环境变量),例如:.dev.vars数字 :METICULOUS_SIDECAR_URL=http://127.0.0.1:9670或者说
wrangler dev --var METICULOUS_SIDECAR_URL:http://127.0.0.1:9670。 。 。 .
没有安装了副车厢的包装是完全的通过,所以安全地用部署的代码离开.
已部署的工人或页面项目的设置
-
将副车开进你的Cloudflare账户,跟着
@alwaysmeticulous/backend-recorder-sidecar-worker读取。 这是一个文件和一个wrangler deploy。 。 。 . -
在您想要录制的每一个环境中, 添加一个对您的应用程序绑定的服务 :
[[services]] binding = "METICULOUS_SIDECAR" service = "meticulous-recorder-sidecar" -
包好你的处理器 对于一个ES模块工人,即
withMeticulous,如上. (中文(简体) ). 对a 云旗页 项目,工人出口onRequest而不是说{ fetch },使用时withMeticulousPagesFunction数字 :import { withMeticulousPagesFunction } from "@alwaysmeticulous/backend-recorder-workerd"; export const onRequest = withMeticulousPagesFunction(handler);应用它 最外侧在你自己的包装 重新写之前
context.env:记录器在env所以先用包装来代替自己的外表 就能掩盖真实的情况. -
如果你的应用程序询问 Postgres —— 超过超驱动程序, 请说 —— 把客户端交给我们, 这样它的询问也会被记录下来 。 在工人中没有自动补丁的接合 :
postgres在建设时被嵌入你的捆绑 并且从来没有通过一个模块加载器.import { withMeticulousPostgres } from "@alwaysmeticulous/backend-recorder-workerd"; const sql = withMeticulousPostgres( postgres(env.HYPERDRIVE.connectionString), );一个呼叫覆盖了隔离区中的每个客户端,它会返回客户端不变. 如果有其它东西也包住客户端, 应用最外侧( Sentry's)
instrumentPostgresJsSql,一个伐木代理).
环境之间没有什么需要改变的:包装器在它找到的侧车上激活,而配置两个侧车都是通过. 绑定胜负 METICULOUS_SIDECAR_URL 当两者同时存在时,因为只有绑定才能被故意添加到部署的wrangler配置中.
一个请求的捕获事件 被分批到 一个 页:1 ctx.waitUntil 在你的答复散去之后——所以,要求打二十通电话需要一次往返,而不是二十通.
捕捉到的东西
- 入境请求 (方法、 URL、 页眉、 状态; 没有机构),通过
x-meticulous-session-id由Meticulous前端记录器所盖的页眉——或者,对于页面的初始导航,通过id shim mints本身(见 第一页渲染的会话 ID) (中文(简体) ). - 发送
fetch电话 在处理输入请求时作出:方法、 URL、 标题、 状态和请求/ 响应机构( UTF-8, 上限为 1 MB; 长寿命流如 SSE 被截取) . - 呼叫通过
fetch- 形状装订 ——服务装订和持久对象活字根——记录如出发取来,外加env键入了电话。 不需要更改代码: 绑定fetch无论应用程序从哪里读到,包括通过cloudflare:workers而不是处理器的env参数. - KV 命名空间操作 —— 说
get, (中文(简体) ),getWithMetadata, (中文(简体) ),put, (中文(简体) ),delete财务报告和审定财务报表list名称空间env,用绑定名称、密钥、调用参数和值记录为JSON(所以JSON.parse重构应用程序所见所闻type(请阅读)。 也不需要更改代码。 一个作为流读取的值从未被记录器读取过——它将从应用中取出字节——而二进制值被跳过;两个大小写都用理由而不是值来记录. - postgres.js 查询当您申请时
withMeticulousPostgres(见上文):SQL与$1-样式占位符,插入参数,行会塑造所要求的查询,以及解析出行——或出错,所以一个查询,合法失败的重放是同一个失败而不是一个缺口. 诅咒,.forEach(),COPY流和.describe()未经记录,因为他们没有结果.
第一页渲染的会话 ID
浏览器不能将自定义头头放入顶级导航上,在开始一个会话的导航上,前端记录器还没有刻出一个ID——只有HTML到达并片段运行之后,才有. 因此,生成您的服务器侧渲染的请求是无法到达的被标记的请求,以及它所做的一切(渲染是自己的) fetch 将完全不记录任何会议.
Shim关闭了另一端的缺口: 对于这样的要求 薄荷会议本身 id,记录下的全部请求,并发布到页面上,页面采纳了它而不是自己磨. 然后整个页面加载在一个会话id下.
当作薄荷. 仅用于一个浏览器装入页面的合理内容: a GET编号 :HEAD 与 Sec-Fetch-Dest: document, (中文(简体) ), iframe 或者说 frame (或,如果没有该标题,则 Accept 包含 text/html(没有入境) x-meticulous-session-id内页 fetch一个RSC导航,一个健康检查和一个爬行器都下降。 它只在录音时发生, 所以一个部署的工人,没有配置的侧车是未受伤害的.
包含一个子帧是因为它不可靠地折叠到上帧的会话中:前端记录器将子帧的数据转发到上帧并压制自己的会话,只有在上帧在同一同物下录入时,所以其父无记录器的跨出源帧本身就是会话——它的渲染需要的id和上层一样多. 子框架在哪里 已经 等待,任何事物都不采用身份标识,任何身份标识都不采用,成本也不高: meticulous.session_id_origin: "backend",摄入量则算不上一个会议没有被采用为无印记而非邮票.
页面如何学习. 两个渠道,要么足够:
Server-Timing: metsession;desc="<id>"回应。 录音机从performance.getEntriesByType("navigation")[0].serverTiming——文档本身的脚本可以读取的一个响应头,它持续了文档的一生,因此不管片段负载晚于何时,这都有效. 不需要更改应用程序 . 限于安全上下文,而缓存或代理可能会剥取.- 你的应用程序通过
getMeticulousSessionId()- 通常为:data-session-id在录音机自己<script>标记 (window.METICULOUS_SESSION_ID(见E/CN.4/Sub.2/2003/L.31)。 精准并免疫于头部缓冲;花费一行.
import {
getMeticulousSessionId,
withMeticulous,
} from "@alwaysmeticulous/backend-recorder-workerd";
export default withMeticulous({
async fetch() {
const sessionId = getMeticulousSessionId();
return new Response(
`<script src="/recorder.js" data-session-id="${escapeHtml(sessionId ?? "")}"></script>`,
{ headers: { "content-type": "text/html" } },
);
},
});
随你如何去掉它,因为你将插入标记:在文件导航上,它是我们自己的薄荷标识,但在任何其他请求下,则是输入页头的逐字记录.
无论是哪种方式,记录器只采用 id,而它的薄荷时间是 现在60多岁左右, 无论是哪个方向 (会议IDs嵌入了他们的薄荷时间). 这种束缚使得CDN或浏览器缓存的响应无法向每个接收它的访客发送一个会话ID. 在窗口内,分享仍然是可能的,所以 hTML 可缓存的应用程序尚不是好候选人 - 关掉薄荷:
export default withMeticulous(handler, { mintProvisionalSessionIds: false });
或设置 METICULOUS_BACKEND_PROVISIONAL_SESSION_IDS var 改为 "false" (一个工人 var/捆绑,像 METICULOUS_SIDECAR_URL ——主机环境变量在工人内部不可见.
getMeticulousSessionId() 也正是id如何到达另一个过程. 文档导航没有入境信头可供您的应用程序转发,因此,一个向自己的服务提供粉丝的工人必须将ID传递给 x-meticulous-session-id 本身;shim不会把它添加到外出的请求中.
请求机构有秘密的JSON领域(clientSecret, (中文(简体) ), apiKey, (中文(简体) ), token, (中文(简体) ), password,)替换为 REDACTED 在他们离开工人之前,因为大量的API在身体中携带证书而不是头像. 反应机构是逐字存储的:当它们为UTF-8时作为文本,否则则作为基准64(标记如下: http.response.body.encoding: "base64"),因此一个二进制响应,如存档或图像重放字节来表示. 一个KV put 数值以同样的方式被编辑,原因相同; 从 KV 读取的值是逐字存储的, 像响应体一样 .
重播
当Meticulous对您的应用程序重放一个会话时, 同样的包装器为回放录音服务: 已退出 fetch 电话是从录音中回答的, 而不是到达真正的服务, 时钟被冻结 在录制的会话结束时 Math.random 编号 : crypto.randomUUID 编号 : crypto.getRandomValues 在每次重播时 应用薄荷都是一样的 重放由"Meticulous"重放跑者注射器头激活,因此不需要额外来启用.
重放为 隐形: 录音不覆盖的调用失败 [backend-recorder] workerd replay: … 错误而不是悄悄地到达了真正的服务,所以录制中的一个漏洞会浮出水面,而不是将回放变成活的流量. 对于一个在重播时必须保持直播的电话,设置 meticulous-passthrough 页眉到 true 应请求:
await fetch(url, { headers: { "meticulous-passthrough": "true" } });
在服务回放时,shim 前缀请求-scoped console.debug, (中文(简体) ), console.log, (中文(简体) ),
console.info, (中文(简体) ), console.warn, (中文(简体) ), console.error,以及 console.trace 输出与活动重放
身份证 每个请求都从 AsyncLocalStorage,所以日志在
一个工人隔离服务 同时重复播放。 启动和重放请求之外的其他工作
没有标记.
通过绑定和KV命名空间的呼叫从录音(参见限制)中得不到服务,并且永远不会失败——它们总是达到真正的绑定.
特工群测试
第三个模式坐落在Meticulous驱动运行(代理会话生成)的重放旁. 它在两个部署舱启动, METICULOUS_AGENT_SWARM_TESTING_SIDECAR_URL 财务报告和审定财务报表 METICULOUS_AGENT_SWARM_TESTING_RUN_ID,由谁部署工人来进行群跑;URL的验证方式与回放侧车URL一样. 每个出 fetch 向拦截者提供URL名称的呼叫服务:拦截者要么提供响应服务,要么通过呼叫传递到真正的服务.
模式通过设计来达到最佳效果——与回放的母调相反. 截取器的呼叫无法回答(漏接,无法接通或行为不当的截取器,shim无法代表的响应)永远不会失败;它只是到达了真正的服务. 没有被冻结的时钟, 没有播种的随机性和记录。 这个 meticulous-passthrough 信头跳过截取器完全用于呼叫.
代码覆盖
Meticulous 可以报告您服务器行中的哪一行被执行的重放会话 。 工人没有可使用的V8覆盖范围——其检查员从未派遣 Profiler.enable这样 Profiler.startPreciseCoverage 无法获取,而一个覆盖则称它做转发报告二进制计数,永不重置,不能按请求拆分。 因此线路被标记在建设时间取而代之.
将插件添加到构建中 上传测试而不是你部署的那个
import { defineConfig } from "vite";
import { meticulousCoverage } from "@alwaysmeticulous/backend-recorder-workerd/vite";
export default defineConfig({
plugins: [meticulousCoverage()],
});
然后回放报告覆盖度自动——包装器将每个请求所运行的行数张贴到Meticulous回放侧式车上,后一回放将它归为会话.
- 仅 服务器 模块是仪器; 除非您通过, 客户端构建是未受影响的
includeClient: true。 。 。 . node_modules和虚拟模块总是被跳过.exclude: [/pattern/]跳过更多.- 被乐器捆绑可以安全地用覆盖来运行:在无物收集时,每个标记都是不开放的,所以同样的图像仍然适用于普通回放和本地开发.
- 插件无法解析的模块不会被修改 。 覆盖是永远不会值得 失败你的建设.
- 有些行是故意不标记的,因为标记它们将意味着重写代码而不是添加到其中:模块上层的语句(它们在孤立启动时运行,在任何请求之外),类
static块,例如if (x) return;,和条件else if(只有第一个)if虽然每个分支机构仍然有自己的标志 因此,即使在执行这些命令的跑道中,这些行也是被揭穿的.
报告行号是来源中的行,不是捆绑中的行. 该插件运行在您所建的 TypeScript/JSX 通过之后,它会重新打印出每个模块,并放下注释和空白行——因此它会解决每行通过该通过源图返回. 有两个后果值得了解:
- 您构建的源图未生成的模块仍然是仪器, 但其行被报告为构建打印 。 插件表示在构建的结尾会影响多少模块.
- 一个模块,其源映射覆盖了其它一些文件,却被完全跳过,而不是对照错误的行报告.
因为每个函数都会重新读取当前请求的槽,所以一个比创建它的请求活取的回调被贷记到实际运行它的请求,而不是构建它的请求.
限制
- 流化反应被截取出:当处理器返回时报告入行请求,对于流化反应,在流化结束前报告出行请求,出行机构上限为1 MB. 其主要终点为SSE的应用程序不会忠实地记录.
- 约束之外的其他
fetch- 形状的和 KV 命名空间没有被捕获: D1, R2, 队列, RPC 方法调用一个命名的切入点(env.SVC.someMethod())——RPC方法并非可补丁属性,因此无法以这种方式被截取. - 非文本的 KV 值不记录其值: 读取 a
stream将字节从应用程序中取出,二进制值被跳过,因为 KV blobs 很大而不是 UTF-8 。 操作、密钥和论据仍被记录下来omitted说它是哪种情况. - 违约时跳过资产约束(
ASSETS, (中文(简体) ),__STATIC_CONTENT:资产字节为大并常为二进制,资产服务通常是工人最高量的调用. 使用options.skipBindings跳过别人. - 出关
fetch向一个数据犬代理,没有前端会话ID,被记录为一无所有. 追踪器在自己的定时器上冲出,所以没有任何东西能将呼叫归为属性,而有效载荷就是它缓冲过的每一个跨度. 目的地只有在其端口为8126财务报告和审定财务报表 它的路径是代理商自己的 API 端点之一(/info, (中文(简体) ),/v0.4/traces还有那个家庭的其他成员,/profiling/v1/input, (中文(简体) ),/telemetry/proxy/…, (中文(简体) ),/evp_proxy/…因为8126和826的应用软件 可能会为任何东西服务/v0.4/traces- 其它地方的形状路径是应用程序自己的终点。 无代理顺从于 adatadoghq.com收录内容不涵盖在内,也没有发出通知。 只记录模式; 重放不受影响 . - 健康探测器被记录为一无是处——而不是入境要求,也不是它所支持的。 进入常规探测路径(
/health, (中文(简体) ),/healthz, (中文(简体) ),/healthcheck, (中文(简体) ),/health-check, (中文(简体) ),/_health, (中文(简体) ),/api/health, (中文(简体) ),/api/healthz, (中文(简体) ),/readyz, (中文(简体) ),/livez, (中文(简体) ),/ping(无)x-meticulous-session-id从不进入抓取语境。 请求其会话名称是真实的 app 流量, 并且无论路径如何被记录, 所以一个 app 仍然可以免费服务/api/health作为页面数据源。 只记录模式; 重放不受影响 . - 持久对象的分贝没有有约束力的名称,因为分贝来自
namespace.get()而不是约束自己. - 第一份文书提出请求之前具有约束力的参考文件
const f = env.SVC.fetch.bind(env.SVC)在模块范围)逃避了仪器化. 保持约束 对象 和电话.fetch()每一请求——SDKs通常做的事情——都被捕获. - WebSocket升级和TCP套接字(
connect())没有被俘. fetch在请求之外打电话的人withMeticulous(如:scheduled处理者)没有被俘.- postgres.js 查询记录,但 没有在工人内部重放d:在"Worker"中制作的一款录音在"Node"上重播的应用程序运行时由"Node"的模拟商店提供回放. 一个工人重放 到达真正的数据库.