☰
Midway 代码染色(Code Dye)实战:一键透视 HTTP 调用链路耗时与出入参
2026/9/29 3:25:59 网站建设 项目流程
  • 后端
  • 微服务
  • 云原生

【免费下载链接】midway

🍔 A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 🌈

项目地址:https://gitcode.com/gh_mirrors/mi/midway
点击查看免费下载

本文介绍 Midway 提供的「代码染色」组件@midwayjs/code-dye:它能在 HTTP 场景下自动记录一次请求内所有方法的调用链路、每个方法的执行时长以及入参和返回值,帮助开发者快速定位“方法执行缓慢”“方法未被执行”“参数传错”等疑难问题。读完本文你将掌握该组件的安装、启用、开关配置与三种染色报告(html / json / log)的完整用法,并理解其基于AsyncLocalStorage的底层实现原理。

背景与适用场景

代码染色适用于@midwayjs/faas、@midwayjs/web、@midwayjs/koa和@midwayjs/express多种框架,是 Midway 在 HTTP 场景下定位代码问题的利器。它解决的核心痛点包括:

  • 代码执行缓慢:不知道具体是哪一个方法拖慢了请求。开启染色后,可以查看每一个方法的执行时长,快速锁定耗时热点。
  • 代码执行错误:
    • 可能是方法根本没有被调用:通过染色报告查看每一个方法的调用链,确认调用是否如期发生;
    • 可能是方法调用参数出错:通过染色报告查看每一个方法的入参和返回值,定位传参或返回的偏差。

该组件对四种框架的支持情况(官方文档声明)如下:

web 支持情况支持
@midwayjs/koa✅
@midwayjs/faas✅
@midwayjs/web✅
@midwayjs/express✅

安装依赖

在项目根目录执行:

$ npm i @midwayjs/code-dye@4 --save

也可以在package.json中手动增加依赖后重新安装:

{ "dependencies": { "@midwayjs/code-dye": "^4.0.0" // ... } }

仓库中该包当前版本为4.2.4,运行环境要求 Node.js >= 20(见 packages/code-dye/package.json)。

启用组件

将 code-dye 组件注册到代码配置中:

// src/configuration.ts import { Configuration } from '@midwayjs/core'; import * as codeDye from '@midwayjs/code-dye'; @Configuration({ imports: [ // ... { component: codeDye, enabledEnvironment: ['local'], // 只在本地启用 } ], }) export class MainConfiguration {}

:::tip

可以在本地或研发环境开启本组件,便于开发时定位问题,但是不建议在线上启用,因为染色会对线上的访问性能产生影响。

:::

从源码实现看,组件在onReady阶段会自动完成两件事(见 packages/code-dye/src/configuration.ts):

  1. 向所有koa、faas、express、egg类型的应用实例插入CodeDyeMW中间件,并置于中间件链最前端,用于拦截请求、判断是否染色并输出报告;
  2. 遍历 IOC 容器中的全部实例,将其path指代的构造方法用染色包装器包裹(codeDye(value.path, ...)),从而让容器里所有方法都被纳入链路采集。

因此,启用组件后无需修改任何业务代码,染色即可作用于整个容器内的方法调用。

配置染色开关

组件默认配置定义在 packages/code-dye/src/config/config.default.ts,共有两个开关参数(类型定义见 packages/code-dye/src/interface.ts):

配置项默认值说明
matchQueryKey'codeDye'请求query中包含该参数名时进入染色链路
matchHeaderKey'codeDye'请求headers中包含该参数名时进入染色链路

通过 query 参数触发染色

可以通过matchQueryKey配置,控制当query参数包含配置对应的值的时候进入染色链路。例如配置为:

// src/config/config.local.ts export default { codeDye: { matchQueryKey: 'codeDyeABC', } }

当请求接口http://127.0.0.1:7001/test?codeDyeABC=html时,组件会判断query中是否存在codeDyeABC参数来决定是否染色,并根据参数对应的值,来响应不同的染色结果。

通过 header 触发染色

也可以通过matchHeaderKey配置,控制当请求头包含配置对应的值的时候进入染色链路。例如配置为:

// src/config/config.local.ts export default { codeDye: { matchHeaderKey: 'codeDyeHeader', } }

当请求接口http://127.0.0.1:7001/test时,组件会判断请求的headers中是否存在codeDyeHeader参数来决定是否染色,并根据参数对应的值,来响应不同的染色结果。

从中间件源码(packages/code-dye/src/middleware.ts)可以确认判断顺序:check()优先读取request.query[matchQueryKey],若 query 未命中,再检查request.headers[matchHeaderKey],命中后取出对应的值作为输出类型。即:同时配置两个开关时,query 优先于 header。

染色报告:html / json / log 三种输出

开启染色后,通过触发开关的参数值即可选择报告输出形式,目前支持以下三种:

输出值行为
html对当前请求的结果进行处理,将染色信息添加到结果中,响应为html,可在浏览器查看
json对当前请求的结果进行处理,将染色信息添加到结果中,响应为json结构化信息
log不对当前请求的结果进行处理,染色信息输出到日志中,不影响请求

例如配置为:

// src/config/config.local.ts export default { codeDye: { matchQueryKey: 'codeDyeXXX', } }

当请求接口http://127.0.0.1:7001/test?codeDyeXXX=html时,组件判断query中codeDyeXXX参数的值为html,就会将染色结果输出在当前请求的响应中,且内容为html格式。

三种模式的响应处理逻辑可在 packages/code-dye/src/middleware.ts 中看到:log模式直接console.log染色 JSON,请求结果原样返回;html模式设置Content-Type: text/html并调用toHTML渲染可视化页面;json模式将完整的调用链 JSON 作为响应体返回。

html 报告中的信息

html模式的可视化页面由 packages/code-dye/src/html.ts 渲染,页面标题为Midway CodeDye,包含:

  • 调用链路总耗时:当前请求从进入到结束的总毫秒数,以及最终的调用结果;
  • 方法调用瀑布图:每个方法按调用层级缩进展示,方法名称为[func] / [async func] / [class] 类名拼接的路径;时间条宽度正比于方法耗时,颜色区分已结束(end)与未结束(not-end)的方法;
  • 入参与返回值:悬停每个方法的「入参」「返回值」按钮,可查看以 JSON 格式美化的参数与返回结果。

json 报告的结构

json模式返回的调用链为嵌套结构,顶层为本次请求的call数组,每个调用节点包含id、paths(方法路径)、start(开始时间与args入参)、end(结束时间与result返回值)以及嵌套的call子节点。该结构同样可被日志模式复用,便于在终端中检索和分析。

底层原理:AsyncLocalStorage 贯穿调用链

染色的核心难点在于:如何在不改动业务代码的前提下,把一个请求生命周期内所有方法调用串成父子调用链。仓库实现选择借助 Node.js 的async_hooks.AsyncLocalStorage(见 packages/code-dye/src/reqInfo.ts):

  • asyncRunWrapper在每个方法调用时以{ codeDyeConfig, codeDyeParent }为上下文执行asyncStorage.run,将当前调用节点挂到父节点的call数组中;
  • 后续的异步方法通过getAsyncInfo()从AsyncLocalStorage中取回当前上下文,从而在跨异步边界后依然能定位到正确的父节点,形成完整链路;
  • 每个调用节点生成全局唯一id(Date.now():自增序号:随机数),并通过Date.now()记录start.time与end.time,据此计算每个方法的执行时长。

包装逻辑位于 packages/code-dye/src/codeDye.ts,它按类型递归处理容器内的实例与函数:

  • 函数(function)→ 包装为codeDyeFuncWrapper,记录入参与返回值;
  • 异步函数(async function)→ 包装为codeDyeAFuncWrapper,同样记录入参与返回值;
  • 类(class)→ 对prototype上的方法递归包装,路径标记为[class] 类名;
  • 对象 / 数组→ 递归遍历其属性并处理 getter / setter,路径分别标记为[prototype get] 属性名、[prototype set] 属性名、[index]下标。

这套机制保证了一个请求内从控制器到服务层、再到类属性的每一次调用都被采集,最终汇聚成可读的调用链报告。

通过测试用例验证

仓库在 packages/code-dye/test/koa.test.ts 中提供了针对 Koa 应用的验证用例,可直接印证上述行为:

  • json 模式:请求/test?codeDye=json,解析响应 JSON,断言调用链中json.call[0].call[3].paths[2]等于'[async func] firstName',且该调用的end.result等于'test'——即染色确实记录到了业务异步方法的路径与返回值;
  • html 模式:请求/test?codeDye=html,断言响应文本非空——即渲染出了可读的染色页面。

可见,默认配置codeDye即为合法的触发参数名,直接访问http://127.0.0.1:7001/test?codeDye=html即可在本地体验染色效果。

总结

@midwayjs/code-dye是一个“零业务侵入”的排障利器:启用组件后,开发与测试环境只需在请求 URL 或 Header 中携带触发参数,即可获得 html / json / log 三种形式的调用链报告,直观看到每个方法的执行时长、调用链结构与出入参。其底层依赖 IOC 容器自动包装与AsyncLocalStorage的异步上下文透传,因此能覆盖控制器到深层服务方法的完整调用路径。建议仅在本地或研发环境开启,并配合matchQueryKey/matchHeaderKey自定义触发开关,以最小成本换取最大的排障效率。

  • 后端
  • 微服务
  • 云原生

【免费下载链接】midway

🍔 A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 🌈

项目地址:https://gitcode.com/gh_mirrors/mi/midway
点击查看免费下载

相关推荐

上一篇:Windows 11 LTSC 安装微软商店:几分钟装回商店,不用手动碰组件
下一篇:如何快速调整Windows窗口大小?WindowResizer 窗口尺寸调整完整指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询