- 后端
- 微服务
- 云原生
【免费下载链接】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. 🌈
本文介绍 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):
- 向所有
koa、faas、express、egg类型的应用实例插入CodeDyeMW中间件,并置于中间件链最前端,用于拦截请求、判断是否染色并输出报告; - 遍历 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. 🌈
相关推荐
Midway code-dye 组件深度解析:基于请求触发的调用链染色与耗时可视化
Midway code dye 组件深度解析:基于请求触发的调用链染色与耗时可视化 @midwayjs/code dye 是 Midway 框架中的一个轻量级调
后端微服务云原生VS Code Copilot 扩展开发之 Visualization Runner:Visualize Test 代码透镜与单条测试调试链路
VS Code Copilot 扩展开发之 Visualization Runner:Visualize Test 代码透镜与单条测试调试链路 本文围绕 VS
开发工具代码编辑器Apache Thrift Node.js 全链路实战:从 IDL 代码生成到 TCP、浏览器与 HTTP 跨语言调用
Apache Thrift Node.js 全链路实战:从 IDL 代码生成到 TCP、浏览器与 HTTP 跨语言调用 本文以 Apache Thrift 仓库
后端微服务API设计
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考