通过OpenTelemetry上报Node.js应用数据

通过OpenTelemetry为应用埋点并上报链路数据至可观测链路 OpenTelemetry 版后,可观测链路 OpenTelemetry 版即可开始监控应用,您可以查看应用拓扑、调用链路、异常事务、慢事务和SQL分析等一系列监控数据。本文介绍如何使用OpenTelemetryNode.js Express应用进行自动或手动埋点并上报数据。

前提条件

支持的运行时环境:

  • OpenTelemetry 目前支持 Node.js v18 及以上版本。

  • 需要注意的是,仅支持 Node.js 的 Active(活跃)或 Maintenance LTS(长期维护)版本。更早的 Node 版本未经过 OpenTelemetry 官方测试,因此不保证能够正常运行。

  • 更多版本兼容性问题,请查看 Supported Runtimes

支持的库和框架:

方法一:自动埋点(推荐)

通过 OpenTelemetry 自动插桩技术,无需修改应用代码即可实现 Node.js 应用的可观测性数据采集并接入云监控2.0。

  1. 运行以下命令来安装 OpenTelemetry API、SDK 和插桩工具。

    npm install --save @opentelemetry/api
    npm install --save @opentelemetry/auto-instrumentations-node
  2. 配置 OpenTelemetry。

    请将${httpEndpoint}替换为HTTP接入点,将${serviceName}替换为您的应用名。

    export OTEL_SERVICE_NAME=${serviceName}
    export OTEL_TRACES_EXPORTER=otlp
    export OTEL_METRICS_EXPORTER=none
    export OTEL_LOGS_EXPORTER=none
    export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=${httpEndpoint}
    export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
    export NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"
    node app.js
    说明

    如要添加更多 OpenTelemetry 环境变量,请查看OpenTelemetry Node.js自动埋点配置

方法二:手动埋点

以下内容使用 OpenTelemetry Node.js SDK 构造 Express 框架应用的 Trace 数据并上传到阿里云可观测链路 OpenTelemetry 版。

步骤一:创建示例应用程序(可选)

这一步将演示如何构建一个简单的 Web 应用程序。如果您已经有编写好的 Node.js 应用程序,请跳过。
  1. 新建一个项目目录,在目录下执行npm init -y命令,会创建一个空的 package.json 文件。

  2. 执行安装依赖包命令:npm install express

  3. 编写应用代码,创建一个名为 app.js 的文件,添加如下内容,这段代码模拟扔骰子游戏,返回1-6之间的一个随机数。

    /*app.js*/
    const express = require('express');
    
    const PORT = parseInt(process.env.PORT || '8080');
    const app = express();
    
    function getRandomNumber(min, max) {
      return Math.floor(Math.random() * (max - min + 1) + min);
    }
    
    app.get('/rolldice', (req, res) => {
      res.send(getRandomNumber(1, 6).toString());
    });
    
    app.listen(PORT, () => {
      console.log(`Listening for requests on http://localhost:${PORT}`);
    });
  4. 此时应用已经编写完成,执行node app.js命令即可运行应用,访问地址为 http://localhost:8080/rolldice

步骤二:导入 OpenTelemetry 相关依赖

  1. 安装 OpenTelemetry Node SDK 和自动插桩包。auto-instrumentations-node 会自动监控代码中常用的第三方库(如 Express),当这些库被调用时自动生成追踪数据。

    npm install @opentelemetry/sdk-node \
      @opentelemetry/api \
      @opentelemetry/auto-instrumentations-node \
      @opentelemetry/sdk-trace-node \
      @opentelemetry/exporter-trace-otlp-proto
  2. 配置 OpenTelemetry:为了让 OpenTelemetry 能够自动收集应用程序的追踪数据并上报到阿里云可观测链路 OpenTelemetry 版,需要创建一个插桩配置文件。该文件负责初始化 SDK、配置数据导出器和启用自动插桩功能。 创建一个名为 instrumentation.js 的文件。

    请将${httpEndpoint}替换为HTTP接入点,请将${serviceName}替换为您的应用名。

    /*instrumentation.js*/
    const opentelemetry = require('@opentelemetry/sdk-node');
    const {
        getNodeAutoInstrumentations,
    } = require('@opentelemetry/auto-instrumentations-node');
    const {
        OTLPTraceExporter,
    } = require('@opentelemetry/exporter-trace-otlp-proto');
    
    const sdk = new opentelemetry.NodeSDK({
        resource: resourceFromAttributes({
          [ATTR_SERVICE_NAME]: '${serviceName}'
        }),
        traceExporter: new OTLPTraceExporter({
            url: "${httpEndpoint}"
        }),
        instrumentations: [getNodeAutoInstrumentations()],
    });
    
    sdk.start();

步骤三:手动创建 Span(可选)

虽然 auto-instrumentations-node 已经能够捕获大部分常见框架和库的调用,但在某些场景下您可能需要手动创建 Span 来获得更细粒度的追踪信息。手动创建 Span 时,需要做以下几件事情:

  • 获取 Tracer: 在应用程序中任何需要手动编写代码创建 Span 的地方,都应该调用 getTracer 来获取一个 tracer。

  • 创建和管理 Span: 在需要创建 Span 的代码中,调用 startActiveSpan 方法来创建一个 span,并使用 end 方法来结束 span。

在原有的app.js上进行修改,支持输出多次扔骰子的结果。以下代码展示了如何为每次掷骰子操作创建独立的 Span,以获得更细粒度的追踪信息。

/*app.js*/
const { trace } = require('@opentelemetry/api');
const express = require('express');
const tracer = trace.getTracer('demo', '0.1.0');

const PORT = parseInt(process.env.PORT || '8080');
const app = express();

function rollOnce(min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min);
}

function rollTheDice(rolls, min, max) {
    // 创建一个 span
    return tracer.startActiveSpan('rollTheDice', (span) => {
        const result = [];
        for (let i = 0; i < rolls; i++) {
            result.push(rollOnce(min, max));
        }
        // 确保结束 span
        span.end();
        return result;
    });
}

app.get('/rolldice', (req, res) => {
    const rolls = req.query.rolls ? parseInt(req.query.rolls.toString()) : NaN;
    if (isNaN(rolls)) {
        res
            .status(400)
            .send("Request parameter 'rolls' is missing or not a number.");
        return;
    }
    res.send(JSON.stringify(rollTheDice(rolls, 1, 6)));
});

app.listen(PORT, () => {
    console.log(`Listening for requests on http://localhost:${PORT}`);
});

步骤四:运行应用

执行命令运行应用:node --require ./instrumentation.js app.js

在控制台查看Trace

可观测链路 OpenTelemetry 版控制台应用列表页面选择目标应用,查看链路数据。