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

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

前提条件

  • 支持的运行时环境:

    • OpenTelemetry 目前支持 Node.js v18 及以上版本。注意,仅支持 Node.js 的 Active(活跃)或 Maintenance LTS(长期维护)版本。虽然更早的 Node 版本可能可以工作,但它们未经过 OpenTelemetry 官方测试,因此不保证能够正常运行。

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

  • 支持的库和框架:许多流行的 Node.js 库都支持自动埋点。完整列表请参见Supported instrumentations

步骤一:获取接入点信息

  1. 登录云监控2.0控制台,选择目标工作空间,在左侧导航栏选择接入中心 > 接入中心

  2. 服务端应用区域单击Node.js卡片,然后选择接入协议类型Opentelemetry

  3. 参数配置区域单击LicenseKey右侧的点击获取,然后根据需求选择埋点方式连接方式上报方式,并输入应用名称版本号部署环境

    页面下方将会根据配置参数生成相应的接入代码,代码中包含了Endpoint、LicenseKey等接入点信息。

    image

设置依赖库

自动埋点(推荐)

  1. 安装依赖包。

    @opentelemetry/api 和 @opentelemetry/auto-instrumentations-node 包用于提供 API、SDK 和插桩工具。

    npm install --save @opentelemetry/api
    npm install --save @opentelemetry/auto-instrumentations-node
  2. 配置以下环境变量,然后启动应用程序。

    HTTP上报

    步骤一获取到的接入点信息替换到以下代码中。

    export OTEL_SERVICE_NAME=<service name>
    export OTEL_RESOURCE_ATTRIBUTES=service.name=<service name>,acs.cms.workspace=<workspace>,service.version=<service version>,deployment.environment=<environment>
    export OTEL_TRACES_EXPORTER=otlp
    export OTEL_LOGS_EXPORTER=none
    export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=<traces.endpoint>
    export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=<metrics.endpoint>
    export OTEL_EXPORTER_OTLP_HEADERS="x-arms-license-key=<license-key>,x-arms-project=<arms-project>,x-cms-workspace=<workspace>"
    export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
    export NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"
    node app.js

    gRPC上报

    步骤一获取到的接入点信息替换到以下代码中。

    export OTEL_SERVICE_NAME=<service name>
    export OTEL_RESOURCE_ATTRIBUTES=service.name=<service name>,acs.cms.workspace=<workspace>,service.version=<service version>,deployment.environment=<environment>
    export OTEL_TRACES_EXPORTER=otlp
    export OTEL_LOGS_EXPORTER=none
    export OTEL_EXPORTER_OTLP_ENDPOINT=<endpoint>
    export OTEL_EXPORTER_OTLP_HEADERS="<license-key>,x-arms-project=<arms-project>,x-cms-workspace=<workspace>"
    export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
    export OTEL_NODE_RESOURCE_DETECTORS="env,host,os"
    export NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"
    node app.js
    说明

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

手动埋点

  1. (可选)创建示例应用程序。

    这一步将演示如何构建一个简单的 Web 应用程序。如果您已经有编写好的 Node.js 应用程序,这一步可跳过。

    1. 新建一个项目目录,在目录下执行以下命令创建一个空的 package.json 文件。

      npm init -y
    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. 此时应用已经编写完成,执行以下命令运行应用,访问地址为http://localhost:8080/rolldice

      node app.js
  2. 导入 OpenTelemetry 相关依赖。

    安装 OpenTelemetry Node SDK 和自动插桩包。

    HTTP上报

    npm install @opentelemetry/sdk-node \
      @opentelemetry/api \
      @opentelemetry/auto-instrumentations-node \
      @opentelemetry/sdk-trace-node \
      @opentelemetry/exporter-trace-otlp-proto

    gRPC上报

    npm install @opentelemetry/sdk-node \
      @opentelemetry/api \
      @opentelemetry/auto-instrumentations-node \
      @opentelemetry/sdk-trace-node \
      @opentelemetry/exporter-trace-otlp-grpc

    auto-instrumentations-node 包会自动监控代码中常用的第三方库(如 Express),当这些库被调用时自动生成追踪数据。

  3. 配置 OpenTelemetry。

    为了让 OpenTelemetry 能够自动收集应用程序的追踪数据并上报到云监控2.0平台,需要创建一个插桩配置文件。该文件负责初始化 SDK、配置数据导出器和启用自动插桩功能。

    创建一个名为 instrumentation.js 的文件,将步骤一获取到的接入点信息替换到以下代码中。

    HTTP上报

    /*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({
        traceExporter: new OTLPTraceExporter({
            url: "<endpoint>",
            headers: {
                'x-arms-license-key': '<license-key>',
                'x-arms-project': '<arms-project>',
                'x-cms-workspace': '<workspace>'
            },
        }),
        instrumentations: [getNodeAutoInstrumentations()],
    });
    
    sdk.start();

    gRPC上报

    /*instrumentation.js*/
    const opentelemetry = require('@opentelemetry/sdk-node');
    const {
        getNodeAutoInstrumentations,
    } = require('@opentelemetry/auto-instrumentations-node');
    const {
        OTLPTraceExporter,
    } = require('@opentelemetry/exporter-trace-otlp-grpc');
    
    const sdk = new opentelemetry.NodeSDK({
        traceExporter: new OTLPTraceExporter({
            url: "<endpoint>",
            headers: {
                'x-arms-license-key': '<license-key>',
                'x-arms-project': '<arms-project>',
                'x-cms-workspace': '<workspace>'
            },
        }),
        instrumentations: [getNodeAutoInstrumentations()],
    });
    
    sdk.start();
  4. (可选)手动创建 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}`);
    });
  5. 运行应用。

    node --require ./instrumentation.js app.js

查看监控数据

  1. 登录云监控2.0控制台,选择目标工作空间,在左侧导航栏选择应用中心 > 运维监控 > 应用监控

  2. 应用列表页面单击目标应用名称,然后查看对应的监控详情。更多信息,请参见应用监控