Node.js

Obtenha telemetria para sua aplicação em menos de 5 minutos!

Esta página mostrará como começar a usar o OpenTelemetry no Node.js.

Você aprenderá como instrumentar rastros e métricas e exibi-los no console.

Pré-requisitos

Certifique-se de que você tenha instalado localmente:

Exemplo de Aplicação

O exemplo a seguir utiliza uma aplicação básica com Express. Caso não esteja utilizando o Express, não se preocupe — você pode usar o OpenTelemetry JavaScript com outros frameworks web, como Koa e Nest.JS. Para uma lista completa de bibliotecas para frameworks suportados, consulte o registro.

Para exemplos mais elaborados, consulte exemplos.

Dependências

Para começar, configure um arquivo vazio package.json em um novo diretório:

npm init -y

Em seguida, instale as dependências do Express.

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

# inicialização do typescript
npx tsc --init
npm install express

Crie e inicie um servidor HTTP

Crie um arquivo chamado app.ts (ou app.js, caso não esteja utilizando TypeScript) e adicione o seguinte código:

/*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 + 1) + min);
}

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

app.listen(PORT, () => {
  console.log(`Aguardando requisições em 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 + 1) + min);
}

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

app.listen(PORT, () => {
  console.log(`Aguardando requisições em http://localhost:${PORT}`);
});

Execute a aplicação utilizando o comando abaixo e acesse http://localhost:8080/rolldice no seu navegador para garantir que esteja funcionando.

$ npx ts-node app.ts
Aguardando requisições em http://localhost:8080
$ node app.js
Aguardando requisições em http://localhost:8080

Instrumentação

A seguir, mostramos como instalar, inicializar e executar uma aplicação instrumentada com OpenTelemetry.

Mais dependências

Primeiro, instale os pacotes do Node SDK e das autoinstrumentações.

O Node SDK permite que você inicialize o OpenTelemetry com diversas configurações padrão que são suficientes para a maioria dos casos de uso.

O pacote auto-instrumentations-node instala as bibliotecas de instrumentação que irão criar automaticamente trechos correspondentes ao código chamado nas bibliotecas. Neste caso, ele fornece instrumentação para o Express, permitindo que a aplicação de exemplo crie trechos automaticamente para cada requisição recebida.

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

Para encontrar todos os módulos de autoinstrumentação, você pode consultar o registro.

Configuração

A configuração e inicialização da instrumentação devem ser executadas antes do código da sua aplicação. Uma ferramenta frequentemente utilizada para essa tarefa é a flag –require.

Crie um arquivo chamado instrumentation.ts (ou instrumentation.js, caso não esteja utilizando TypeScript), que deverá conter o código de configuração de instrumentação.

/*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*/
// Requer dependências
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();

Execute a aplicação instrumentada

Agora você poderá executar a aplicação normalmente, mas poderá usar a flag --require para carregar a instrumentação antes do código da aplicação. Certifique-se de que não haja conflitos na utilização da flag --require, como, por exemplo, a variável de ambiente NODE_OPTIONS já possuir algo como --require @opentelemetry/auto-instrumentations-node/register.

$ npx ts-node --require ./instrumentation.ts app.ts
Aguardando requisições em http://localhost:8080
$ node --require ./instrumentation.js app.js
Aguardando requisições em http://localhost:8080

Acesse http://localhost:8080/rolldice no seu navegador e recarregue a página algumas vezes. Depois de um tempo, você deverá ver os trechos exibidos no console pelo ConsoleSpanExporter.

Ver exemplo de saída
{
  "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": []
}

O trecho gerado rastreia o tempo de vida de uma requisição para a rota /rolldice.

Envie mais algumas requisições para esta rota. Depois de um tempo, você poderá visualizar métricas na saída do console, como as seguintes:

Ver exemplo de saída
{
  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: []
}

Próximos passos

Enriqueça a instrumentação gerada automaticamente com a instrumentação manual na sua base de código. Isso lhe proporcionará dados de observabilidade personalizados.

Você também poderá configurar um exportador apropriado para exportar seus dados de telemetria para um ou mais backends de telemetria.

Caso queira explorar um exemplo mais complexo, dê uma olhada no OpenTelemetry Demo, que inclui o Serviço de Pagamento em JavaScript e o Serviço Frontend em TypeScript.

Soluções de problemas

Algo deu errado? Você pode habilitar o logging de diagnóstico para validar se o OpenTelemetry está inicializado corretamente:

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

// Para solução de problemas, defina o nível de log como DiagLogLevel.DEBUG
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO);

// const sdk = new NodeSDK({...
/*instrumentation.js*/
// Requer dependências
const { diag, DiagConsoleLogger, DiagLogLevel } = require('@opentelemetry/api');

// Para solução de problemas, defina o nível de log como DiagLogLevel.DEBUG
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO);

// const sdk = new NodeSDK({...

Last modified January 31, 2025: [CI] Fix fix:dict and run it (#6143) (0d38602e)