NocoBase 服务端 Telemetry 遥测开发指南:基于 OpenTelemetry 的指标采集与链路追踪
【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase
NocoBase 的遥测(Telemetry)模块基于 OpenTelemetry 封装,为插件开发者提供了一套统一、可插拔的可观测性 API,用于收集监控指标(Metric)和链路追踪(Trace)数据。本文将围绕 docs/docs/cn/plugin-development/server/telemetry.md 展开,结合 packages/core/telemetry 的源码实现,讲解如何在插件中完成指标插桩、链路插桩、插桩工具库注册,以及如何对接采集器(MetricReader / SpanProcessor),帮助你为 NocoBase 服务端构建完整的可观测性方案。
注意:该功能目前为实验性功能,API 与行为可能随版本演进调整。
Telemetry 模块架构概览
NocoBase 的遥测模块独立存在于 packages/core/telemetry/src/index.ts,对外导出Telemetry、Metric、Trace三个核心类,并透传 OpenTelemetry 生态的类型与工具(如PeriodicExportingMetricReader、Meter)。三者职责分明:
| 类 | 职责 | 对应 OpenTelemetry 概念 |
|---|---|---|
Telemetry | 顶层入口,持有trace与metric两个子模块,负责插桩库注册与全局资源(Resource)构建 | Instrumentation、Resource |
Trace | 管理NodeTracerProvider与SpanProcessor注册,提供getTracer() | Tracer、SpanProcessor |
Metric | 管理MeterProvider与MetricReader注册,提供getMeter() | Meter、MetricReader |
在 packages/core/server/src/application.ts#L1300-L1304 中,Application构造时会创建Telemetry实例,并自动注入appName(应用名)与version(当前 NocoBase 版本号),之后暴露为app.telemetry。也就是说,在插件中可以通过app.telemetry直接访问metric与trace两个子模块。
遥测模块的生命周期与Application紧密绑定(application.ts#L617-L618、application.ts#L711-L715):
app.beforeLoad阶段:telemetry.init()被调用,注册所有插桩库,构建 Service Resource;app.beforeLoad之后:若配置telemetry.enabled为真,则调用telemetry.start()启动数据导出;- 应用关闭时:调用
telemetry.shutdown()优雅停止所有 Processor 与 Reader。
从源码结构看(telemetry.ts),init()内部通过registerInstrumentations注册插桩库,并用resourceFromAttributes构建包含service.name、service.version与app.name三个属性的 Resource,随后依次初始化Trace与Metric。
指标(Metric)插桩
获取 Meter 并创建计数器
指标插桩的第一步是从app.telemetry.metric获取Meter,再通过Meter创建各类指标仪器(Counter、Histogram、UpDownCounter 等):
const meter = app.telemetry.metric.getMeter(); const counter = meter.createCounter('event_counter', {}); counter.add(1);getMeter()的默认标识为nocobase-meter,版本为当前 NocoBase 版本(见 metric.ts);createCounter('event_counter', {})创建一个名为event_counter的单调递增计数器,业务代码每次发生对应事件时调用counter.add(1)累加。
Metric 的底层实现细节
从 metric.ts 的源码可以看到几个关键行为:
- 默认 Reader 已内置:
Metric构造时自动注册了名为console的PeriodicExportingMetricReader,其 exporter 为ConsoleMetricExporter,并指定AggregationTemporality.DELTA(增量聚合)。也就是说,不做任何配置时指标会周期性打印到控制台; - 环境变量过滤指标:
start()时会读取TELEMETRY_METRICS环境变量(逗号分隔的指标名列表),通过 OpenTelemetry 的 Metric View 机制,仅保留列表中出现的指标,其余全部用AggregationType.DROP丢弃(metric.ts#L75-L94)。该机制可用于在生产环境控制指标上报的开销; - 多 Reader 支持:
readerName既可以是单个字符串,也可以是逗号分隔的多个名称,start()时会逐个实例化并挂载到MeterProvider(metric.ts#L96-L114)。
指标数据采集(导出到控制台)
采集器(Reader)决定指标最终流向何处。注册自定义 Reader 同样通过registerReader完成,下面示例将指标导出到控制台:
import { Plugin } from '@nocobase/server'; import { PeriodicExportingMetricReader, ConsoleMetricExporter, } from '@opentelemetry/sdk-metrics'; class MetricReaderPlugin extends Plugin { afterAdd() { this.app.on('beforeLoad', (app) => { app.telemetry.metric.registerReader( 'console', () => new PeriodicExportingMetricReader({ exporter: new ConsoleMetricExporter(), }), ); }); } }要点说明:
registerReader(name, getReader)的第一个参数是 Reader 的唯一标识,第二个参数是返回MetricReader实例的工厂函数(见 metric.ts#L62-L64);PeriodicExportingMetricReader会按固定时间间隔拉取指标并交给 exporter 导出;ConsoleMetricExporter将数据以 JSON 形式打印到标准输出,适合本地调试;- 若要切换实际生效的 Reader,可通过
MetricOptions.readerName(例如应用配置中的TELEMETRY_METRIC_READER环境变量)指定,start()时会按名称从注册表中取出对应的工厂并实例化。未找到对应名称时会被安全跳过(continue)。
链路(Trace)插桩
获取 Tracer 并创建 Span
链路追踪用于记录一次请求或任务在分布式系统中的完整调用链。NocoBase 中通过app.telemetry.trace获取Tracer,然后开启 Span:
const tracer = app.telemetry.trace.getTracer(); tracer.startActiveSpan(); tracer.startSpan();getTracer()默认返回名为nocobase-trace的 Tracer(见 trace.ts#L86-L91);startActiveSpan()会创建 Span 并将其设为当前活动上下文(需配合回调使用),适合在一个函数/请求的作用域内追踪;startSpan()则仅创建 Span,不自动设置活动上下文,适合手动管理 span 生命周期与父子关系。
Trace 的底层实现细节
从 trace.ts 源码可知:
- 默认 Processor 已内置:
Trace构造时自动注册名为console的BatchSpanProcessor,exporter 为ConsoleSpanExporter,Span 会批量导出到控制台; - NodeTracerProvider 注册为全局:
start()中创建NodeTracerProvider并调用provider.register()(trace.ts#L82-L83),使整个 Node.js 进程(包括未显式获取 Tracer 的第三方库)都能接入同一链路上下文; - 多 Processor 支持:
processorName支持逗号分隔的多个名称,start()时逐个实例化并传入NodeTracerConfig(trace.ts#L58-L80)。
链路数据采集(导出到控制台)
Span 的导出由SpanProcessor决定。注册自定义 Processor 的完整示例:
import { Plugin } from '@nocobase/server'; import { BatchSpanProcessor, ConsoleSpanExporter, } from '@opentelemetry/sdk-trace-base'; class TraceSpanProcessorPlugin extends Plugin { afterAdd() { this.app.on('beforeLoad', (app) => { app.telemetry.trace.registerProcessor( 'console', () => new BatchSpanProcessor(new ConsoleSpanExporter()), ); }); } }要点说明:
registerProcessor(name, getProcessor)注册后,可通过TraceOptions.processorName(或环境变量TELEMETRY_TRACE_PROCESSOR)指定启用哪个 Processor(见 trace.ts#L45-L51);BatchSpanProcessor会将 Span 缓存后批量发送,避免每条 Span 都产生一次 I/O;ConsoleSpanExporter则把 Span 打印到控制台,便于开发期观察链路结构;- 实际生产环境中,可在此处替换为 OTLP、Jaeger 等 exporter,并分别通过
registerReader/registerProcessor以「注册 + 名称启用」的方式接入。
插桩工具库(Instrumentation)注册
OpenTelemetry 生态提供了大量自动化插桩库(如 HTTP、数据库客户端、消息队列等),NocoBase 通过app.telemetry.addInstrumentation()批量注册:
import { Plugin } from '@nocobase/server'; import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; class InstrumentationPlugin extends Plugin { afterAdd() { this.app.on('beforeLoad', (app) => { app.telemetry.addInstrumentation(getNodeAutoInstrumentations()); }); } }getNodeAutoInstrumentations()会返回一组 Node.js 常用插桩库的集合,一次性覆盖常见 I/O 场景;addInstrumentation(...)在内部将这些插桩库推入Telemetry.instrumentations数组(见 telemetry.ts#L70-L72),并在init()时通过registerInstrumentations统一生效。
插桩库的适用边界(重要限制)
:::warning 注意
NocoBase 中遥测模块的初始化位置为app.beforeLoad,因此并不是所有插桩库都适用于 NocoBase。
比如 instrumentation-koa 需要在Koa实例化之前引入,而 NocoBase 的Application虽然基于Koa,但遥测模块是在Application实例化之后才初始化的,所以无法使用。
:::
这一限制的根源在于遥测模块的生命周期设计:Telemetry实例在Application构造函数中创建(application.ts#L1300-L1304),而插桩库的注册发生在beforeLoad钩子中。凡是在「模块加载期」就必须对目标库进行 monkey-patch 的插桩(典型的如 Koa、Express 等 Web 框架插桩),都因错过实例化时机而无法生效。因此,建议优先选择在运行时按调用点进行拦截的插桩库,并始终以目标插桩库的文档说明其初始化时机要求为准。
应用级配置与环境变量
在 packages/core/app/src/config/telemetry.ts 中,NocoBase 通过环境变量提供了一套开箱即用的遥测配置:
| 环境变量 | 作用 | 默认值 / 示例 |
|---|---|---|
TELEMETRY_ENABLED | 是否启用遥测数据导出,值为on时启用 | 未设置时不启动导出 |
TELEMETRY_SERVICE_NAME | 遥测服务名(对应 Resource 的service.name) | nocobase |
TELEMETRY_METRIC_READER | 指定启用的MetricReader名称(对应metric.readerName) | console |
TELEMETRY_TRACE_PROCESSOR | 指定启用的SpanProcessor名称(对应trace.processorName) | console |
TELEMETRY_METRICS | 逗号分隔的指标名白名单,仅这些指标会被收集 | 空(收集全部) |
对应的AppTelemetryOptions结构(见 application.ts 中telemetry配置类型):
export interface TelemetryOptions { serviceName?: string; // 服务名,默认 'nocobase' appName?: string; // 应用名,由 Application 自动注入 version?: string; // 版本号,由 Application 自动注入 trace?: TraceOptions; // tracerName / version / processorName metric?: MetricOptions; // meterName / version / readerName }部署时可据此在启动脚本中组合使用,例如:
TELEMETRY_ENABLED=on \ TELEMETRY_SERVICE_NAME=nocobase-prod \ TELEMETRY_METRIC_READER=console \ TELEMETRY_TRACE_PROCESSOR=console \ TELEMETRY_METRICS=event_counter,http_request_duration \ yarn start插件开发中的推荐实践
综合文档与源码,在 NocoBase 插件中接入遥测的推荐模式如下:
- 在
afterAdd()中挂载beforeLoad监听:因为遥测模块在app.beforeLoad阶段初始化,插桩与采集器的注册都应放在this.app.on('beforeLoad', ...)回调中执行,确保注册先于init(); - 注册命名工厂而非实例:
registerReader/registerProcessor的第二个参数是工厂函数(每次启动时调用),这样同一个名称可以按启动配置动态创建不同实例; - 用环境变量控制采集目标:通过
TELEMETRY_METRIC_READER、TELEMETRY_TRACE_PROCESSOR、TELEMETRY_METRICS实现「同一套代码、不同环境不同采集策略」,避免在代码中写死导出目标; - 与 Logger 配合:遥测负责结构性数据(指标、链路),日志负责事件详情,二者结合才能构成完整的可观测性方案,可参考 Logger 日志 与 服务端开发概述;
- 善用事件机制:
beforeLoad属于 NocoBase 应用生命周期事件,更多事件用法见 Event 事件;若需要在中间件中追踪请求链路,可参考 Middleware 中间件 并在中间件内通过app.telemetry.trace开启 Span。
相关文档导航
- Logger 日志 — 日志与遥测配合使用,完善可观测性方案
- Plugin 插件 — 在插件中注册遥测插桩和采集器
- 服务端开发概述 — 遥测模块在服务端架构中的位置
- Event 事件 — 通过事件机制在
beforeLoad中初始化遥测 - Middleware 中间件 — 在中间件中结合遥测追踪请求链路
- Telemetry API 参考 —
Telemetry类方法完整签名与默认值 - Trace API 参考 —
Trace类方法完整签名与默认值 - Metric API 参考 —
Metric类方法完整签名与默认值 - 核心源码:packages/core/telemetry/src/telemetry.ts、packages/core/telemetry/src/trace.ts、packages/core/telemetry/src/metric.ts
【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考