OpenTelemetry Node-JS¶
OpenTelemetry Node.js 自动插桩模块通过启动参数预加载,在应用依赖被加载前注册 Hook。无需修改业务代码,即可采集受支持的 Web 框架、HTTP 客户端、数据库和消息队列等调用。
除了标准 OpenTelemetry 能力外,观测云还为 Node.js 提供 Profile 扩展能力。该能力基于 @cloudcare/profiler-nodejs 和 @datadog/pprof,可以采集 wall、heap profile,并通过 DataKit 的 /profiling/v1/input 接口上报。Profile 扩展不是 OpenTelemetry 官方标准能力,本文将其作为零代码接入之外的补充方案保留。
本文使用 DataKit 的 OpenTelemetry 采集器接收 OTLP 数据并转发到观测云:
前置条件¶
- 使用 OpenTelemetry JavaScript 当前支持的 Node.js LTS 版本;
- 应用使用 npm、pnpm 或 Yarn 管理依赖;
- 已安装 DataKit,且 DataKit 已连接到目标观测云工作空间;
- Node.js 应用到 DataKit 的网络可达:OTLP/HTTP 使用 DataKit HTTP 端口
9529,OTLP/gRPC 默认使用4317。
版本与扩展支持¶
- Node.js:自动插桩包通常要求
^18.19.0或>=20.6.0,生产环境建议使用当前仍受支持的 LTS 版本,并以实际安装包的engines声明为准; - OpenTelemetry:建议使用当前稳定版本,并通过 lockfile 固定实际部署版本;
- Profile 扩展包:
@cloudcare/profiler-nodejs; - 默认 Profiling 上报地址:
http://127.0.0.1:9529/profiling/v1/input。
一、开启 OpenTelemetry 采集器¶
进入 DataKit 安装目录下的 conf.d/opentelemetry。如果尚未创建采集器配置,复制示例文件:
确认 opentelemetry.conf 至少包含以下接收配置:
[[inputs.opentelemetry]]
# 将需要在观测云中作为标签保留的自定义属性加入白名单。
customer_tags = ["team", "project"]
[inputs.opentelemetry.http]
http_status_ok = 200
trace_api = "/otel/v1/traces"
metric_api = "/otel/v1/metrics"
logs_api = "/otel/v1/logs"
[inputs.opentelemetry.grpc]
addr = "127.0.0.1:4317"
max_payload = 16777216
接收地址如下:
| 协议 | 数据类型 | DataKit 接收地址 |
|---|---|---|
| OTLP/HTTP + Protobuf | Trace | http://<DataKit-IP>:9529/otel/v1/traces |
| OTLP/HTTP + Protobuf | Metric | http://<DataKit-IP>:9529/otel/v1/metrics |
| OTLP/HTTP + Protobuf | Log | http://<DataKit-IP>:9529/otel/v1/logs |
| OTLP/gRPC | Trace、Metric、Log | http://<DataKit-IP>:4317 |
如果 Node.js 应用与 DataKit 不在同一主机,需按实际部署调整 DataKit 监听地址、防火墙或其他网络访问控制。gRPC 可将 addr 改为应用可访问的监听地址,例如 0.0.0.0:4317。不要将 OTLP 接收端口直接暴露到公网。
重启 DataKit 并检查服务:
二、应用接入 OpenTelemetry¶
安装自动插桩模块¶
在应用项目目录中,从 npm 官方仓库安装 OpenTelemetry 官方包:
@opentelemetry/auto-instrumentations-node 包含 Node.js SDK、自动插桩库和常用 exporter。它只会为已支持且实际加载的库生成遥测数据;应用使用的框架或客户端不在支持列表中时,不会自动产生对应 Span。
通过环境变量启动¶
下面以 OTLP/HTTP + Protobuf 为例,默认接入 Trace,Metric 和 Log 暂时关闭:
export OTEL_SERVICE_NAME="order-service"
export OTEL_RESOURCE_ATTRIBUTES="env=prod,version=1.0.0,team=backend"
export OTEL_TRACES_EXPORTER="otlp"
export OTEL_METRICS_EXPORTER="none"
export OTEL_LOGS_EXPORTER="none"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
export OTEL_EXPORTER_OTLP_ENDPOINT="http://127.0.0.1:9529/otel"
export OTEL_PROPAGATORS="tracecontext,baggage"
export NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"
node app.js
OTEL_EXPORTER_OTLP_ENDPOINT 是基础地址。OTLP/HTTP exporter 会根据数据类型自动追加 /v1/traces、/v1/metrics 或 /v1/logs,最终对应 DataKit 的 /otel/v1/* 路由。
如果应用已有 NODE_OPTIONS,需要在保留原参数的基础上追加 --require @opentelemetry/auto-instrumentations-node/register,不要直接覆盖原值。PM2、systemd、Supervisor 或 npm scripts 场景中,应将 OTEL_* 与 NODE_OPTIONS 写入实际应用进程的启动环境。
通过启动参数加载¶
也可以不设置 NODE_OPTIONS,直接在 Node.js 启动命令中预加载模块:
自动插桩模块必须在应用代码及其依赖加载之前执行。不要在 node app.js 启动后再动态加载该模块,否则已经加载的库可能无法被插桩。
常见启动方式¶
通过 npm script 启动:
通过 PM2 启动时,可将变量放入 ecosystem.config.js 的 env 配置,或通过进程管理平台的环境变量功能注入。修改配置后必须重启应用进程,仅执行热重载不一定会重新加载预加载模块。
使用 OTLP/gRPC¶
如需改用 OTLP/gRPC,只需替换协议和 endpoint:
export OTEL_EXPORTER_OTLP_PROTOCOL="grpc"
export OTEL_EXPORTER_OTLP_ENDPOINT="http://127.0.0.1:4317"
gRPC endpoint 不能追加 /v1/traces 等 HTTP 路径。
三、数据上报参数¶
基础参数¶
| 环境变量 | 说明 | 建议值或示例 |
|---|---|---|
OTEL_SERVICE_NAME |
设置 service.name;未设置时服务无法被稳定识别。 |
order-service,生产环境必须显式设置。 |
OTEL_RESOURCE_ATTRIBUTES |
资源属性,格式为逗号分隔的 key=value。 |
env=prod,version=1.0.0,team=backend |
OTEL_TRACES_EXPORTER |
Trace 导出器。 | 上报到 DataKit 时设为 otlp;关闭时设为 none。 |
OTEL_METRICS_EXPORTER |
Metric 导出器。 | 需要上报指标时设为 otlp,否则设为 none。 |
OTEL_LOGS_EXPORTER |
Log 导出器。 | 需要上报日志时设为 otlp,否则设为 none。 |
OTEL_PROPAGATORS |
跨服务上下文传播格式。 | 默认 tracecontext,baggage;全链路应保持兼容。 |
OTEL_SDK_DISABLED |
禁用 OpenTelemetry SDK。 | 默认 false;应急关闭时设为 true。 |
service.name 用于观测云中的服务归属;建议同时设置 env 和 version,用于按环境和版本筛选。其他自定义资源属性需要加入 DataKit customer_tags 白名单后才会作为标签保留,属性名中的 . 会转换为 _。
Node.js 自动插桩参数¶
| 环境变量 | 说明 | 默认值或示例 |
|---|---|---|
NODE_OPTIONS |
在应用依赖加载前预加载自动插桩注册模块。 | --require @opentelemetry/auto-instrumentations-node/register |
OTEL_NODE_RESOURCE_DETECTORS |
启用指定资源探测器,多个名称用逗号分隔。 | 默认 all;可设为 env,host,os,process 或 none。 |
OTEL_NODE_ENABLED_INSTRUMENTATIONS |
只启用列出的插桩,名称不带 @opentelemetry/instrumentation- 前缀。 |
http,express,pg |
OTEL_NODE_DISABLED_INSTRUMENTATIONS |
从默认列表中禁用指定插桩。 | fs,grpc |
OTEL_LOG_LEVEL |
OpenTelemetry 内部诊断日志级别。 | 生产环境建议 info;排障时短暂使用 debug。 |
同时设置启用和禁用列表时,先应用 OTEL_NODE_ENABLED_INSTRUMENTATIONS,再应用禁用列表;同一个插桩出现在两个列表中时,最终会被禁用。复杂的单个插桩配置不支持完全通过环境变量完成,超出本文零代码接入范围。
OTLP 参数¶
| 环境变量 | 说明 | 建议值或示例 |
|---|---|---|
OTEL_EXPORTER_OTLP_PROTOCOL |
所有信号的 OTLP 协议。 | DataKit 支持 http/protobuf 或 grpc。 |
OTEL_EXPORTER_OTLP_ENDPOINT |
所有信号共用的基础地址。 | HTTP:http://datakit-host:9529/otel;gRPC:http://datakit-host:4317。 |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT |
仅 Trace 使用的地址,优先于共用地址。 | http://datakit-host:9529/otel/v1/traces |
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT |
仅 Metric 使用的地址,优先于共用地址。 | http://datakit-host:9529/otel/v1/metrics |
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT |
仅 Log 使用的地址,优先于共用地址。 | http://datakit-host:9529/otel/v1/logs |
OTEL_EXPORTER_OTLP_HEADERS |
所有 OTLP 请求携带的请求头,多个值用逗号分隔。 | x-tenant=tenant-a;需与 DataKit expected_headers 一致。 |
OTEL_EXPORTER_OTLP_COMPRESSION |
OTLP 请求压缩方式。 | 跨主机上报可设为 gzip。 |
OTEL_EXPORTER_OTLP_TIMEOUT |
单次导出超时,单位毫秒。 | 10000 |
DataKit 的 OTLP/HTTP 采集仅支持 Protobuf,请使用 http/protobuf,不要使用 http/json。统一 endpoint 与某类数据的 endpoint 同时存在时,某类数据的配置优先。
采样和批量上报参数¶
| 环境变量 | 说明 | 默认值或示例 |
|---|---|---|
OTEL_TRACES_SAMPLER |
Trace 头部采样器。 | 默认 parentbased_always_on;按比例采样使用 parentbased_traceidratio。 |
OTEL_TRACES_SAMPLER_ARG |
采样器参数。 | 0.1 表示根 Trace 采样 10%。 |
OTEL_BSP_SCHEDULE_DELAY |
Span 批量导出间隔,单位毫秒。 | 默认 5000。 |
OTEL_BSP_MAX_QUEUE_SIZE |
待导出的 Span 队列上限。 | 默认 2048。 |
OTEL_BSP_MAX_EXPORT_BATCH_SIZE |
每批最多导出的 Span 数。 | 默认 512。 |
OTEL_BSP_EXPORT_TIMEOUT |
Span 批量导出超时,单位毫秒。 | 默认 30000。 |
OTEL_METRIC_EXPORT_INTERVAL |
Metric 导出间隔,单位毫秒。 | 默认 60000。 |
生产环境应根据流量和数据预算设置采样率。应用侧头部采样与 DataKit 侧采样同时开启时,最终保留率会叠加降低,应统一规划采样位置。
补充:代码方式接入¶
本文推荐优先使用前面的零代码方式。如果需要精确控制 Resource、Exporter 或单个插桩,也可以保留原有的 SDK 代码接入方式。
安装依赖¶
npm install --save \
@opentelemetry/api \
@opentelemetry/sdk-node \
@opentelemetry/resources \
@opentelemetry/exporter-trace-otlp-proto \
@opentelemetry/instrumentation-http
示例代码¶
OpenTelemetry 必须在业务模块加载之前初始化:
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { resourceFromAttributes } = require('@opentelemetry/resources');
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
const {
OTLPTraceExporter,
} = require('@opentelemetry/exporter-trace-otlp-proto');
const resource = resourceFromAttributes({
'service.name': 'orders-api',
'service.version': '1.0.0',
'deployment.environment.name': 'prod',
});
const sdk = new NodeSDK({
resource,
traceExporter: new OTLPTraceExporter({
url: 'http://127.0.0.1:9529/otel/v1/traces',
}),
instrumentations: [new HttpInstrumentation()],
});
async function main() {
await sdk.start();
require('./app');
}
process.once('SIGTERM', async () => {
await sdk.shutdown();
process.exit(0);
});
main().catch(error => {
console.error(error);
process.exit(1);
});
代码方式与 NODE_OPTIONS=--require .../register 不应重复初始化两套 SDK。选择代码方式后,应移除零代码注册模块的预加载参数。
补充:Node.js Profile 扩展¶
Node.js Profile 用于补充标准 OpenTelemetry Trace 和 Metric 之外的性能剖析数据。当前支持的 profile 类型:
wall;heap。
实际接入时,建议先启用 wall,确认采集开销和上报链路稳定后再按需开启 heap。
安装 Profile 扩展¶
最小接入示例¶
const { resourceFromAttributes } = require('@opentelemetry/resources');
const {
DatakitProfilingExporter,
NodeProfiling,
} = require('@cloudcare/profiler-nodejs');
const profiler = new NodeProfiling({
resource: resourceFromAttributes({
'service.name': 'orders-api',
'service.version': '1.2.3',
'deployment.environment.name': 'prod',
}),
exporter: new DatakitProfilingExporter({
endpoint: 'http://127.0.0.1:9529/profiling/v1/input',
}),
profileTypes: ['wall'],
cpuProfilingEnabled: true,
});
async function main() {
await profiler.start();
require('./app');
}
process.once('SIGTERM', async () => {
await profiler.shutdown();
process.exit(0);
});
main().catch(error => {
console.error(error);
process.exit(1);
});
与 OpenTelemetry SDK 一起使用¶
如果应用采用上一节的 SDK 代码方式,可以复用同一个 Resource:
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { resourceFromAttributes } = require('@opentelemetry/resources');
const {
DatakitProfilingExporter,
NodeProfiling,
} = require('@cloudcare/profiler-nodejs');
const resource = resourceFromAttributes({
'service.name': 'orders-api',
'service.version': '1.2.3',
'deployment.environment.name': 'prod',
});
const sdk = new NodeSDK({ resource });
const profiling = new NodeProfiling({
resource,
exporter: new DatakitProfilingExporter({
endpoint: 'http://127.0.0.1:9529/profiling/v1/input',
}),
});
async function main() {
await sdk.start();
await profiling.start();
require('./app');
}
process.once('SIGTERM', async () => {
await profiling.shutdown();
await sdk.shutdown();
process.exit(0);
});
main().catch(error => {
console.error(error);
process.exit(1);
});
验证链路时,可以手动触发一次采集:
Profile 推荐配置¶
| 参数 | 默认值 | 说明 |
|---|---|---|
endpoint |
http://127.0.0.1:9529/profiling/v1/input |
Profile 上传地址。 |
profileTypes |
['wall', 'heap'] |
采集类型,建议先使用 ['wall']。 |
intervalMillis |
60000 |
周期采集间隔。 |
wallDurationMillis |
10000 |
单次 wall profile 持续时间。 |
默认情况下,扩展每 60 秒执行一次采集,wall profile 持续 10 秒,并将数据发送到 /profiling/v1/input。当前 exporter 会发送 wall.pprof、space.pprof 和描述本次采集的 event.json;其中 profiler 为 ddtrace、family 为 nodejs、format 为 pprof,用于兼容观测云的 Node.js Profile 解析链路。
如果运行环境没有 globalThis.fetch,需要显式提供 fetch 实现。为避免进程退出前 profile 丢失,应在终止信号处理中调用 shutdown()。如果只需要 Trace 和 Metric,无需安装 Profile 扩展。
验证接入¶
请求一个经过已支持插桩库处理的应用路由,然后在 DataKit 主机检查接收日志:
出现 /otel/v1/traces 的 POST 请求且响应码为 200,表示 DataKit 已接收 Trace。然后进入观测云的「应用性能监测 > 链路」按 service:order-service 查询。
如果没有数据,依次检查:自动插桩包是否安装在应用项目中、NODE_OPTIONS 是否传递给实际 Node.js 进程、注册模块是否在应用依赖前加载、代码路径是否命中受支持的插桩库,以及 OTLP endpoint 是否可达。排障时可临时设置 OTEL_LOG_LEVEL=debug,确认完成后恢复为 info。
如需验证 Profile 接口可达性,可执行:
Profile 上报成功时,扩展调试日志中通常会出现 Datakit profiling export succeeded。随后可在观测云中按服务检查 Node.js Profile 数据。