跳转至

OpenTelemetry Node-JS

OpenTelemetry Node.js 自动插桩模块通过启动参数预加载,在应用依赖被加载前注册 Hook。无需修改业务代码,即可采集受支持的 Web 框架、HTTP 客户端、数据库和消息队列等调用。

除了标准 OpenTelemetry 能力外,观测云还为 Node.js 提供 Profile 扩展能力。该能力基于 @cloudcare/profiler-nodejs@datadog/pprof,可以采集 wallheap profile,并通过 DataKit 的 /profiling/v1/input 接口上报。Profile 扩展不是 OpenTelemetry 官方标准能力,本文将其作为零代码接入之外的补充方案保留。

本文使用 DataKit 的 OpenTelemetry 采集器接收 OTLP 数据并转发到观测云:

Node.js 应用 + OpenTelemetry 自动插桩模块 -> OTLP -> DataKit -> 观测云

前置条件

  • 使用 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。如果尚未创建采集器配置,复制示例文件:

cd /usr/local/datakit/conf.d/opentelemetry
sudo cp opentelemetry.conf.sample opentelemetry.conf

确认 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 并检查服务:

sudo datakit service restart
curl http://127.0.0.1:9529/v1/ping

二、应用接入 OpenTelemetry

安装自动插桩模块

在应用项目目录中,从 npm 官方仓库安装 OpenTelemetry 官方包:

npm install --save \
  @opentelemetry/api \
  @opentelemetry/auto-instrumentations-node

@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 --require @opentelemetry/auto-instrumentations-node/register app.js

自动插桩模块必须在应用代码及其依赖加载之前执行。不要在 node app.js 启动后再动态加载该模块,否则已经加载的库可能无法被插桩。

常见启动方式

通过 npm script 启动:

NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register" npm start

通过 PM2 启动时,可将变量放入 ecosystem.config.jsenv 配置,或通过进程管理平台的环境变量功能注入。修改配置后必须重启应用进程,仅执行热重载不一定会重新加载预加载模块。

使用 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 用于观测云中的服务归属;建议同时设置 envversion,用于按环境和版本筛选。其他自定义资源属性需要加入 DataKit customer_tags 白名单后才会作为标签保留,属性名中的 . 会转换为 _

Node.js 自动插桩参数

环境变量 说明 默认值或示例
NODE_OPTIONS 在应用依赖加载前预加载自动插桩注册模块。 --require @opentelemetry/auto-instrumentations-node/register
OTEL_NODE_RESOURCE_DETECTORS 启用指定资源探测器,多个名称用逗号分隔。 默认 all;可设为 env,host,os,processnone
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/protobufgrpc
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 扩展

npm install --save \
  @cloudcare/profiler-nodejs \
  @datadog/pprof \
  @opentelemetry/resources

最小接入示例

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);
});

验证链路时,可以手动触发一次采集:

await profiling.collectOnce();

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.pprofspace.pprof 和描述本次采集的 event.json;其中 profilerddtracefamilynodejsformatpprof,用于兼容观测云的 Node.js Profile 解析链路。

如果运行环境没有 globalThis.fetch,需要显式提供 fetch 实现。为避免进程退出前 profile 丢失,应在终止信号处理中调用 shutdown()。如果只需要 Trace 和 Metric,无需安装 Profile 扩展。

验证接入

请求一个经过已支持插桩库处理的应用路由,然后在 DataKit 主机检查接收日志:

curl http://127.0.0.1:8080/
sudo tail -f /usr/local/datakit/log/gin.log | grep '/otel/v1/'

出现 /otel/v1/tracesPOST 请求且响应码为 200,表示 DataKit 已接收 Trace。然后进入观测云的「应用性能监测 > 链路」按 service:order-service 查询。

如果没有数据,依次检查:自动插桩包是否安装在应用项目中、NODE_OPTIONS 是否传递给实际 Node.js 进程、注册模块是否在应用依赖前加载、代码路径是否命中受支持的插桩库,以及 OTLP endpoint 是否可达。排障时可临时设置 OTEL_LOG_LEVEL=debug,确认完成后恢复为 info

如需验证 Profile 接口可达性,可执行:

curl -i http://127.0.0.1:9529/profiling/v1/input

Profile 上报成功时,扩展调试日志中通常会出现 Datakit profiling export succeeded。随后可在观测云中按服务检查 Node.js Profile 数据。

参考

文档评价

文档内容是否对您有帮助? ×