Node.js
本文将向您展示如何在Node.js中开始使用OpenTelemetry。
您将学习如何对[跟踪(traces)][] 和[指标(metrics)][] 进行仪表化,并将它们记录到控制台中。
注意
OpenTelemetry 的适用于 Node.js 的日志记录库仍在开发中,因此本文没有提供示例。有关有关 OpenTelemetry 在 JavaScript 中的状态的更多信息,请查看 这里。准备工作
确保您已在本地安装了以下内容:
- Node.js
- TypeScript(如果您将使用TypeScript)。
示例应用程序
以下示例使用了一个基本的 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({...