Node.js

在不到5分钟内获得应用程序的遥测数据!

本文将向您展示如何在Node.js中开始使用OpenTelemetry。

您将学习如何对[跟踪(traces)][] 和[指标(metrics)][] 进行仪表化,并将它们记录到控制台中。

准备工作

确保您已在本地安装了以下内容:

示例应用程序

以下示例使用了一个基本的 Express 应用程序。如果您不使用 Express,也没有关系 — 您同样可以在其他Web框架(如 Koa 和 Nest.js)中使用 OpenTelemetry JavaScript。有关支持的框架所使用的库的完整列表,请参见注册表

有关更复杂的示例,请参阅示例

依赖项

首先,在新目录中创建一个空的 package.json 文件:

npm init -y

接下来,安装 Express 相关的依赖项。

npm install typescript \
  ts-node \
  @types/node \
  express \
  @types/express

# 初始化 TypeScript
npx tsc --init
npm install express

创建和启动 HTTP 服务器

创建一个名为 app.ts(如果不使用 TypeScript,则为 app.js)的文件,并将以下代码添加到其中:

/*app.ts*/
import express, { Express } from 'express';

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

function getRandomNumber(min: number, max: number) {
  return Math.floor(Math.random() * (max - min) + min);
}

app.get('/rolldice', (req, res) => {
  res.send(getRandomNumber(1, 6).toString());
});

app.listen(PORT, () => {
  console.log(`监听请求:http://localhost:${PORT}`);
});
/*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) + min);
}

app.get('/rolldice', (req, res) => {
  res.send(getRandomNumber(1, 6).toString());
});

app.listen(PORT, () => {
  console.log(`监听请求:http://localhost:${PORT}`);
});

使用以下命令运行应用程序,并在您的Web浏览器中打开http://localhost:8080/rolldice以确保其正常工作。

$ npx ts-node app.ts
监听请求:http://localhost:8080
$ node app.js
监听请求:http://localhost:8080

仪表化

下面演示了如何安装、初始化和运行使用OpenTelemetry进行仪表化的应用程序。

更多依赖项

首先,安装 Node SDK 和自动仪表化包。

Node SDK 允许您使用几个配置默认项初始化 OpenTelemetry,这些默认项对于大多数用例都是正确的。

auto-instrumentations-node 包提供了对 Express 的仪表化,它会自动创建与调用库中的代码对应的跨度。在这种情况下,它提供了 Express 的仪表化,让示例应用程序自动为每个传入的请求创建跨度。

npm install @opentelemetry/sdk-node \
  @opentelemetry/api \
  @opentelemetry/auto-instrumentations-node \
  @opentelemetry/sdk-metrics

要找到所有自动仪表化模块,您可以查看注册表

设置

仪表化设置和配置必须在应用程序代码之前运行。一个常用于此任务的工具是–require标志。

创建一个名为 instrumentation.ts(如果不使用 TypeScript,则为 instrumentation.js)的文件,其中包含您的仪表化设置代码。

/*instrumentation.ts*/
import { NodeSDK } from '@opentelemetry/sdk-node';
import { ConsoleSpanExporter } from '@opentelemetry/sdk-trace-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import {
  PeriodicExportingMetricReader,
  ConsoleMetricExporter,
} from '@opentelemetry/sdk-metrics';

const sdk = new NodeSDK({
  traceExporter: new ConsoleSpanExporter(),
  metricReader: new PeriodicExportingMetricReader({
    exporter: new ConsoleMetricExporter(),
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();
/*instrumentation.js*/
// 引入依赖项
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { ConsoleSpanExporter } = require('@opentelemetry/sdk-trace-node');
const {
  getNodeAutoInstrumentations,
} = require('@opentelemetry/auto-instrumentations-node');
const {
  PeriodicExportingMetricReader,
  ConsoleMetricExporter,
} = require('@opentelemetry/sdk-metrics');

const sdk = new NodeSDK({
  traceExporter: new ConsoleSpanExporter(),
  metricReader: new PeriodicExportingMetricReader({
    exporter: new ConsoleMetricExporter(),
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();

运行已仪表化的应用程序

现在,您可以像往常一样运行应用程序,但可以使用 --require 标志在应用程序代码之前加载仪表化设置。

$ npx ts-node --require ./instrumentation.ts app.ts
监听请求:http://localhost:8080
$ node --require ./instrumentation.js app.js
监听请求:http://localhost:8080

在您的Web浏览器中打开http://localhost:8080/rolldice并重新加载页面几次。过一段时间后,您应该在控制台中看到由 ConsoleSpanExporter 打印的跨度。

查看示例输出
{
  "traceId": "3f1fe6256ea46d19ec3ca97b3409ad6d",
  "parentId": "f0b7b340dd6e08a7",
  "name": "middleware - query",
  "id": "41a27f331c7bfed3",
  "kind": 0,
  "timestamp": 1624982589722992,
  "duration": 417,
  "attributes": {
    "http.route": "/",
    "express.name": "query",
    "express.type": "middleware"
  },
  "status": { "code": 0 },
  "events": []
}
{
  "traceId": "3f1fe6256ea46d19ec3ca97b3409ad6d",
  "parentId": "f0b7b340dd6e08a7",
  "name": "middleware - expressInit",
  "id": "e0ed537a699f652a",
  "kind": 0,
  "timestamp": 1624982589725778,
  "duration": 673,
  "attributes": {
    "http.route": "/",
    "express.name": "expressInit",
    "express.type": "middleware"
  },
  "status": { code: 0 },
  "events": []
}
{
  "traceId": "3f1fe6256ea46d19ec3ca97b3409ad6d",
  "parentId": "f0b7b340dd6e08a7",
  "name": "request handler - /",
  "id": "8614a81e1847b7ef",
  "kind": 0,
  "timestamp": 1624982589726941,
  "duration": 21,
  "attributes": {
    "http.route": "/",
    "express.name": "/",
    "express.type": "request_handler"
  },
  "status": { code: 0 },
  "events": []
}
{
  "traceId": "3f1fe6256ea46d19ec3ca97b3409ad6d",
  "parentId": undefined,
  "name": "GET /",
  "id": "f0b7b340dd6e08a7",
  "kind": 1,
  "timestamp": 1624982589720260,
  "duration": 11380,
  "attributes": {
    "http.url": "http://localhost:8080/",
    "http.host": "localhost:8080",
    "net.host.name": "localhost",
    "http.method": "GET",
    "http.route": "",
    "http.target": "/",
    "http.user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36",
    "http.flavor": "1.1",
    "net.transport": "ip_tcp",
    "net.host.ip": "::1",
    "net.host.port": 8080,
    "net.peer.ip": "::1",
    "net.peer.port": 61520,
    "http.status_code": 304,
    "http.status_text": "NOT MODIFIED"
  },
  "status": { "code": 1 },
  "events": []
}

生成的跨度会跟踪对 /rolldice 路由的请求的生命周期。

向该端点发送更多请求。片刻后,您将在控制台输出中看到指标,如下所示:

查看示例输出
{
  descriptor: {
    name: 'http.server.duration',
    type: 'HISTOGRAM',
    description: 'measures the duration of the inbound HTTP requests',
    unit: 'ms',
    valueType: 1
  },
  dataPointType: 0,
  dataPoints: [
    {
      attributes: [Object],
      startTime: [Array],
      endTime: [Array],
      value: [Object]
    }
  ]
}
{
  descriptor: {
    name: 'http.client.duration',
    type: 'HISTOGRAM',
    description: 'measures the duration of the outbound HTTP requests',
    unit: 'ms',
    valueType: 1
  },
  dataPointType: 0,
  dataPoints: []
}
{
  descriptor: {
    name: 'db.client.connections.usage',
    type: 'UP_DOWN_COUNTER',
    description: 'The number of connections that are currently in the state referenced by the attribute "state".',
    unit: '{connections}',
    valueType: 1
  },
  dataPointType: 3,
  dataPoints: []
}
{
  descriptor: {
    name: 'http.server.duration',
    type: 'HISTOGRAM',
    description: 'measures the duration of the inbound HTTP requests',
    unit: 'ms',
    valueType: 1
  },
  dataPointType: 0,
  dataPoints: [
    {
      attributes: [Object],
      startTime: [Array],
      endTime: [Array],
      value: [Object]
    }
  ]
}
{
  descriptor: {
    name: 'http.client.duration',
    type: 'HISTOGRAM',
    description: 'measures the duration of the outbound HTTP requests',
    unit: 'ms',
    valueType: 1
  },
  dataPointType: 0,
  dataPoints: []
}
{
  descriptor: {
    name: 'db.client.connections.usage',
    type: 'UP_DOWN_COUNTER',
    description: 'The number of connections that are currently in the state referenced by the attribute "state".',
    unit: '{connections}',
    valueType: 1
  },
  dataPointType: 3,
  dataPoints: []
}
{
  descriptor: {
    name: 'http.server.duration',
    type: 'HISTOGRAM',
    description: 'measures the duration of the inbound HTTP requests',
    unit: 'ms',
    valueType: 1
  },
  dataPointType: 0,
  dataPoints: [
    {
      attributes: [Object],
      startTime: [Array],
      endTime: [Array],
      value: [Object]
    }
  ]
}
{
  descriptor: {
    name: 'http.client.duration',
    type: 'HISTOGRAM',
    description: 'measures the duration of the outbound HTTP requests',
    unit: 'ms',
    valueType: 1
  },
  dataPointType: 0,
  dataPoints: []
}
{
  descriptor: {
    name: 'db.client.connections.usage',
    type: 'UP_DOWN_COUNTER',
    description: 'The number of connections that are currently in the state referenced by the attribute "state".',
    unit: '{connections}',
    valueType: 1
  },
  dataPointType: 3,
  dataPoints: []
}

下一步

使用手动仪表化您自己的代码库,可以为自动生成的仪表化数据增加额外功能。这将为你提供定制的可观察性数据。

您还需要配置适当的导出器,以将您的遥测数据导出到一个或多个遥测后端。

如果您想探索一个更复杂的示例,请查看OpenTelemetry演示,其中包括基于JavaScript的支付服务和基于TypeScript的前端服务

故障排除

发生了什么错误吗?您可以启用诊断日志记录以验证是否正确初始化了OpenTelemetry:

/*instrumentation.ts*/
import { diag, DiagConsoleLogger, DiagLogLevel } from '@opentelemetry/api';

// 调试时,将日志级别设置为 DiagLogLevel.DEBUG
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO);

// const sdk = new NodeSDK({...
/*instrumentation.js*/
// 引入依赖项
const { diag, DiagConsoleLogger, DiagLogLevel } = require('@opentelemetry/api');

// 调试时,将日志级别设置为 DiagLogLevel.DEBUG
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO);

// const sdk = new NodeSDK({...
最后修改 December 10, 2023: translate (a4350d6e)