一、这个方法解决什么问题
trace_events.createTracing()用于创建一个Tracing对象,该对象绑定一组追踪类别(categories)。创建出来的对象处于禁用状态,需要调用tracing.enable()才会将绑定的类别加入全局已启用追踪类别,调用tracing.disable()则将这些类别从中移除。
它的价值在于运行时粒度控制。相比--trace-event-categories启动标志一次性开启所有需要的类别,createTracing允许代码在特定逻辑段前后精确地开关追踪。例如,只在一个可疑的异步操作周围启用node.async_hooks,操作结束后立即禁用,减少无关事件对追踪日志的干扰。
一个需要先明确的背景:node:trace_events模块在当前Node.js文档中仍标注为Stability:1-Experimental。这意味着API的形态和输出格式可能在版本间调整。在生产环境依赖它做长期监控之前,需要在目标Node.js版本的CI中验证行为一致性。
二、categories参数的规则与常见错误
createTracing(options)接受一个对象,其categories属性是字符串数组。官方文档对数组元素的处理规则是:值会尽可能被强制转换为字符串,如果无法转换则抛出错误。
如果传入空数组或不传categories,会抛出ERR_TRACE_EVENTS_CATEGORY_REQUIRED,提示该方法至少需要一个追踪事件类别。这个错误码在测试中可以用来验证参数校验逻辑。
可用的类别名称包括node、node.async_hooks、node.perf、node.fs.sync、node.fs.async、v8、node.http等。类别名称拼写错误不会抛出错误,但对应的追踪数据不会产生。这意味着如果发现chrome://tracing中看不到预期的事件,第一步检查的是类别名是否与文档列表一致,而不是代码逻辑。
三、enable与disable的叠加语义
这是Tracing对象最容易误解的部分。tracing.disable()不会无条件关闭它绑定的所有类别。官方文档的规则是:只有那些“没有被其他已启用的Tracing对象覆盖,且没有被--trace-event-categories标志指定”的类别才会被禁用。
考虑两个Tracing对象共享node类别的情况:
const t1 = trace_events.createTracing({ categories: ['node', 'v8'] });
const t2 = trace_events.createTracing({ categories: ['node.perf', 'node'] });
t1.enable();
t2.enable();
// 已启用:node, node.perf, v8
t2.disable();
// 已启用:node, v8
// node 仍然启用,因为 t1 也覆盖了它
这个语义在分层追踪或插件化架构中有实际意义:一个模块启用node类别做基础追踪,另一个模块叠加node.perf做性能测量。后者禁用时,前者的node追踪不受影响。如果代码中假设disable()会清空所有绑定类别,在多个Tracing对象共存的场景中会出现与预期不符的行为。
四、与--trace-event-categories标志的职责边界
启动标志和模块方法作用于同一套全局类别,但生命周期不同。标志指定的类别在整个进程生命周期内持续启用,createTracing创建的类别可以动态开关。两者可以共存:标志启用的类别不会被任何Tracing对象的disable()关闭。
trace_events.getEnabledCategories()返回的是当前所有来源(已启用的Tracing对象∪命令行标志)的并集,以逗号分隔的字符串形式呈现。在排查“为什么某个类别的数据出现了/没出现”时,这个方法比逐个检查Tracing对象状态更直接。
五、示例:分段追踪与类别叠加验证
以下示例演示创建两个Tracing对象、验证叠加语义,并在enable期间执行一段可观察的操作。示例中的code_number为代码号学习编程的标识。
const trace_events = require('node:trace_events');
// 对象 A:基础类别
const tracingA = trace_events.createTracing({
categories: ['node.perf', 'node']
});
// 对象 B:共享 node,额外覆盖 async_hooks
const tracingB = trace_events.createTracing({
categories: ['node.async_hooks', 'node']
});
console.log('创建后 A.enabled:', tracingA.enabled);
console.log('创建后 B.enabled:', tracingB.enabled);
tracingA.enable();
console.log('A 启用后类别:', trace_events.getEnabledCategories());
tracingB.enable();
console.log('B 启用后类别:', trace_events.getEnabledCategories());
// 在追踪窗口内执行操作
const start = performance.now();
for (let i = 0; i < 1e5; i++) { /* code_number 模拟负载 */ }
console.log('操作耗时:', (performance.now() - start).toFixed(2), 'ms');
// 禁用 B,node 应仍被 A 覆盖而保持启用
tracingB.disable();
console.log('B 禁用后类别:', trace_events.getEnabledCategories());
console.log('B.enabled:', tracingB.enabled);
tracingA.disable();
console.log('A 禁用后类别:', trace_events.getEnabledCategories());
运行这段代码,观察getEnabledCategories()的输出变化。B禁用后node仍然存在,这是叠加语义的直接验证。tracing.enabled属性反映的是该对象自身的启用状态,而非其覆盖类别的全局状态。
追踪日志默认写入node_trace.${rotation}.log,可以通过--trace-event-file-pattern自定义文件名模板,支持${pid}和${rotation}占位符。生成的日志文件可以在Chrome的chrome://tracing页面加载查看。
六、边界情况与常见错误
空类别数组:抛出ERR_TRACE_EVENTS_CATEGORY_REQUIRED。测试参数校验时可以用assert.throws验证。
类别值无法转换为字符串:传入Symbol作为类别元素会触发转换错误。实际使用中传入对象或数字会通过隐式toString转换,但建议始终传入明确的字符串,避免类别名与预期不一致。
重复enable:对一个已经启用的Tracing对象再次调用enable()不会产生额外效果,也不会抛出错误。类别仍然只被计入一次。
Worker线程中不可用:trace_events模块的功能在Worker线程中不可用。在worker内部尝试require('node:trace_events')或调用相关方法不会产生主线程的追踪效果。
进程退出时的日志完整性:如果进程因信号退出,追踪日志可能需要正确的信号处理才能完整刷盘。官方文档建议为SIGINT、SIGTERM等信号设置处理函数,确保追踪系统有机会完成写入。
七、与getEnabledCategories()的配合排查
当追踪结果不符合预期时,getEnabledCategories()是优选的诊断工具。它返回的字符串直接反映了当前全局启用的类别,不关心这些类别来自哪个Tracing对象或哪个命令行标志。
一个常见的排查顺序是:先在enable()之后立即调用getEnabledCategories(),确认类别确实被加入;再在操作结束后检查追踪日志中是否有对应类别的事件。如果第一步正常但第二步没有数据,问题可能在于类别本身在当前Node.js版本中不产生事件(部分类别在不同版本间存在增减),而非createTracing的调用方式。
八、常见误区速查
以为createTracing创建后就自动开始追踪。创建时对象处于禁用状态,必须调用enable()。
以为disable()会关闭所有绑定类别。只关闭未被其他已启用Tracing对象覆盖、且未被命令行标志指定的类别。
以为类别名拼错会报错。不会。拼错的类别不会产生追踪数据,但不会在创建或启用时抛出错误。
以为追踪日志会自动在任意位置生成。需要启用追踪并执行操作,日志默认写入工作目录下的node_trace.*.log,文件名可通过命令行标志调整。
九、练习与思考
-
创建两个
Tracing对象,第一个覆盖['node.perf','v8'],第二个覆盖['node.async_hooks','v8']。先启用第一个,再启用第二个,然后只禁用第一个。getEnabledCategories()的输出是什么?v8是否仍然启用? -
在
tracing.enable()之后、执行操作之前,进程收到SIGTERM退出。追踪日志中是否会包含操作期间的事件?这与信号处理函数的设置有什么关系? -
如果目标Node.js版本中某个类别名称已不存在(例如版本升级后类别被重命名),
createTracing和enable()会有什么行为?如何在不依赖类别名正确性的前提下验证Tracing对象本身的生命周期?
十、延伸阅读与参考文献
Node.js官方文档Tracing对象,nodejs.cn中文镜像,说明Tracing对象创建时处于禁用状态,以及enable()和disable()对类别的具体操作语义。https://url.nodejs.cn/api/v25/tracing/tracing.html
Node.js官方文档Traceevents,nodejs.orgv26,提供完整类别列表、--trace-event-categories标志用法、getEnabledCategories()的并集语义以及disable()不关闭共享类别的示例。https://nodejs.org/api/tracing.html
Node.js官方文档ERR_TRACE_EVENTS_CATEGORY_REQUIRED,nodejs.cn中文镜像,说明createTracing()至少需要一个追踪事件类别。https://url.nodejs.cn/api/v18/errors/err_trace_events_category_required.html
Node.js官方文档tracing.disable(),nodejs.cn中文镜像,专门说明只有未被其他已启用Tracing对象覆盖且未被命令行标志指定的类别才会被禁用,附t1/t2共享node类别的示例。https://url.nodejs.cn/dist/latest-v18.x/docs/api/tracing/tracing_disable.html
Node.js文档源码doc/api/tracing.md,GitHub nodejs/node,包含node.threadpoolwork.sync、node.http、node.module_timer等较新类别的定义,以及追踪日志文件命名和信号处理建议。https://github.com/nodejs/node/blob/159ae48f/doc/api/tracing.md