Gerar traces e métricas com o Node.js

Neste documento, descrevemos como modificar um app JavaScript de Node.js para coletar dados de rastreamento e métrica usando o framework de código aberto OpenTelemetry, e como gravar registros JSON estruturados para saída padrão. Este documento também fornece informações sobre um aplicativo Node.js de exemplo que pode ser instalado e executado. O app usa o framework da Web Fastify e está configurado para gerar métricas, traces e registros.

Para saber mais sobre instrumentação, consulte os seguintes documentos:

Sobre a instrumentação manual e automática

Para essa linguagem, o OpenTelemetry define a instrumentação automática como a prática de coletar telemetria de bibliotecas e frameworks sem fazer alterações no código. No entanto, você tem módulos de instalação e define variáveis de ambiente.

Este documento não descreve as informações automáticas. Para mais informações sobre esse tópico, consulte Instrumentação automática para nó.

Para informações gerais, consulte Instrumentação do OpenTelemetry para o Node.

Antes de começar

Enable the Cloud Logging, Cloud Monitoring, and Cloud Trace APIs.

Enable the APIs

Instrumentar o app para coletar traces, métricas e registros

Para instrumentar o app para coletar dados de rastreamento e métrica e gravar um JSON estruturado para saída padrão, siga as etapas a seguir, conforme descrito nas próximas seções deste documento:

  1. Configurar o OpenTelemetry
  2. Configurar seu app para pré-carregar a configuração do OpenTelemetry
  3. Configurar a geração de registros estruturados
  4. Gravar registros estruturados

Configurar o OpenTelemetry

A configuração padrão do SDK do Node.js para OpenTelemetry exporta traces usando o protocolo OTLP. Ela também configura o OpenTelemetry para usar o formato Contexto de Rastreamento do W3C para propagar o contexto do trace. Essa configuração garante que os períodos tenham o relacionamento pai-filho correto em um trace.

O exemplo de código a seguir ilustra um módulo JavaScript para configurar o OpenTelemetry.

Para ver o exemplo completo, clique em Mais e selecione Ver no GitHub.


diag.setLogger(
  new DiagConsoleLogger(),
  opentelemetry.core.getEnv().OTEL_LOG_LEVEL
);

const sdk = new opentelemetry.NodeSDK({
  instrumentations: getNodeAutoInstrumentations({
    // Disable noisy instrumentations
    '@opentelemetry/instrumentation-fs': {enabled: false},
  }),
  resourceDetectors: getResourceDetectorsFromEnv(),
  metricReader: getMetricReader(),
});

try {
  sdk.start();
  diag.info('OpenTelemetry automatic instrumentation started successfully');
} catch (error) {
  diag.error(
    'Error initializing OpenTelemetry SDK. Your application is not instrumented and will not produce telemetry',
    error
  );
}

// Gracefully shut down the SDK to flush telemetry when the program exits
process.on('SIGTERM', () => {
  sdk
    .shutdown()
    .then(() => diag.debug('OpenTelemetry SDK terminated'))
    .catch(error => diag.error('Error terminating OpenTelemetry SDK', error));
});

O exemplo de código anterior configura o OpenTelemetry para exportar métricas usando o protocolo OTLP e usa o pacote @opentelemetry/auto-instrumentations-node para configurar todas as instrumentações Node.js disponíveis.

Para garantir que toda a telemetria pendente seja transferida e que as conexões sejam fechadas corretamente antes do encerramento do aplicativo, o gerenciador SIGTERM chama shutdown.

Para mais informações e opções de configuração, consulte Instrumentação automática do Node.js para OpenTelemetry.

Configurar seu app para pré-carregar a configuração do OpenTelemetry

Para configurar o app para gravar registros estruturados e coletar métricas e rastrear dados com o OpenTelemetry, atualize a invocação do app para pré-carregar o módulo de instrumentação com a flag --require do Node.js. O uso da flag --require garante que o OpenTelemetry seja inicializado antes que o app seja iniciado. Para mais informações, consulte Primeiras etapas do Node.js para OpenTelemetry.

O exemplo de código a seguir ilustra um Dockerfile transmitindo a flag --require:

CMD node --require ./build/src/instrumentation.js build/src/index.js 2>&1 | tee /var/log/app.log

Configurar a geração de registros estruturados

Para incluir as informações de trace como parte dos registros formatados em JSON gravados na saída padrão, configure seu app para gerar registros estruturados no formato JSON. O Fastify usa o framework de registro do Pino e fornece um registrador em cada gerenciador de solicitações. O exemplo de código a seguir ilustra um objeto LoggerOptions do Pino que configura o app para gerar registros estruturados JSON:


// Expected attributes that OpenTelemetry adds to correlate logs with spans
interface LogRecord {
  trace_id?: string;
  span_id?: string;
  trace_flags?: string;
  [key: string]: unknown;
}

// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity
const PinoLevelToSeverityLookup: Record<string, string | undefined> = {
  trace: 'DEBUG',
  debug: 'DEBUG',
  info: 'INFO',
  warn: 'WARNING',
  error: 'ERROR',
  fatal: 'CRITICAL',
};

export const loggerConfig = {
  messageKey: 'message',
  // Same as pino.stdTimeFunctions.isoTime but uses "timestamp" key instead of "time"
  timestamp(): string {
    return `,"timestamp":"${new Date(Date.now()).toISOString()}"`;
  },
  formatters: {
    log(object: LogRecord): Record<string, unknown> {
      // Add trace context attributes following Cloud Logging structured log format described
      // in https://cloud.google.com/logging/docs/structured-logging#special-payload-fields
      const {trace_id, span_id, trace_flags, ...rest} = object;

      return {
        'logging.googleapis.com/trace': trace_id,
        'logging.googleapis.com/spanId': span_id,
        'logging.googleapis.com/trace_sampled': trace_flags
          ? trace_flags === '01'
          : undefined,
        ...rest,
      };
    },
    // See
    // https://getpino.io/#/docs/help?id=mapping-pino-log-levels-to-google-cloud-logging-stackdriver-severity-levels
    level(label: string) {
      return {
        severity:
          PinoLevelToSeverityLookup[label] ?? PinoLevelToSeverityLookup['info'],
      };
    },
  },
} satisfies LoggerOptions;

A configuração anterior extrai informações sobre o período ativo da mensagem de registro e, em seguida, adiciona essas informações como atributos ao registro estruturado JSON. Esses atributos podem ser usados para correlacionar um registro com um trace:

  • logging.googleapis.com/trace: nome do recurso do trace associado à entrada de registro.
  • logging.googleapis.com/spanId: o ID do período com o trace associado à entrada de registro.
  • logging.googleapis.com/trace_sampled: o valor desse campo precisa ser true ou false.

Para mais informações sobre esses campos, consulte a estrutura LogEntry.

Para usar a configuração do Pino com o Fastify, transmita o objeto de configuração do logger ao criar o app Fastify:

// Create the Fastify app providing the Pino logger config
const fastify = Fastify({
  logger: loggerConfig,
});

Gravar registros estruturados

Para gravar registros estruturados que vinculam a um trace, use o logger do Pino fornecido pelo Fastify. Por exemplo, a instrução a seguir mostra como chamar o método Logger.info():

request.log.info({subRequests}, 'handle /multi request');

O OpenTelemetry preenche automaticamente as entradas de registro do Pino com o contexto do período do período ativo atual no OpenTelemetry Context. Esse contexto de período é incluído nos registros JSON, conforme descrito em Configurar a geração de registros estruturados.

Executar um app de exemplo configurado para coletar telemetria

O app de exemplo usa formatos neutros em relação a fornecedores, incluindo JSON para registros e OTLP para métricas e traces, além do framework do Fastify. Para rotear a telemetria para o Google Cloud, este exemplo usa o Collector do OpenTelemetry configurado com os exportadores do Google. O app tem dois endpoints:

  • O endpoint /multi é processado pela função handleMulti. O gerador de carga no app emite solicitações para o endpoint /multi. Quando esse endpoint recebe uma solicitação, ele envia de três a sete solicitações para o endpoint /single no servidor local.

    /**
     * handleMulti handles an http request by making 3-7 http requests to the /single endpoint.
     *
     * OpenTelemetry instrumentation requires no changes here. It will automatically generate a
     * span for the handler body.
     */
    fastify.get('/multi', async request => {
      const subRequests = randInt(3, 8);
      request.log.info({subRequests}, 'handle /multi request');
    
      for (let i = 0; i < subRequests; i++) {
        await axios.get(`http://localhost:${port}/single`);
      }
      return 'ok';
    });
  • O endpoint /single é processado pela função handleSingle. Quando esse endpoint recebe uma solicitação, ele fica suspenso por um pequeno atraso e depois responde com uma string.

    /**
     * handleSingle handles an http request by sleeping for 100-200 ms. It writes the number of
     * milliseconds slept as its response.
     */
    fastify.get('/single', async request => {
      // Sleep between 100-200 milliseconds
      const sleepMillis = randInt(100, 200);
      request.log.info({sleepMillis}, 'Going to sleep');
      await sleep(sleepMillis);
      return `slept ${sleepMillis}\n`;
    });

Fazer o download e implantar o app

Para executar a amostra:

  1. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

    At the bottom of the Google Cloud console, a Cloud Shell session starts and displays a command-line prompt. Cloud Shell is a shell environment with the Google Cloud CLI already installed and with values already set for your current project. It can take a few seconds for the session to initialize.

  2. Clone o repositório:

    git clone https://github.com/GoogleCloudPlatform/opentelemetry-operations-js
    
  3. Acesse o diretório da amostra:

    cd opentelemetry-operations-js/samples/instrumentation-quickstart
    
  4. Crie e execute a amostra:

    docker compose up --abort-on-container-exit
    

    Se você não estiver usando o Cloud Shell, execute o aplicativo com a variável de ambiente GOOGLE_APPLICATION_CREDENTIALS apontando para um arquivo de credenciais. O Application Default Credentials fornece um arquivo de credenciais em $HOME/.config/gcloud/application_default_credentials.json.

    # Set environment variables
    export GOOGLE_CLOUD_PROJECT="PROJECT_ID"
    export GOOGLE_APPLICATION_CREDENTIALS="$HOME/.config/gcloud/application_default_credentials.json"
    export USERID="$(id -u)"
    
    # Run
    docker compose -f docker-compose.yaml -f docker-compose.creds.yaml up --abort-on-container-exit
    

Ver suas métricas

A instrumentação do OpenTelemetry no app de amostra gera métricas do Prometheus, que podem ser visualizadas usando o Metrics Explorer:

  • Prometheus/http_server_duration_milliseconds/histogram registra a duração das solicitações do servidor e armazena os resultados em um histograma.

  • Prometheus/http_client_duration_milliseconds/histogram registra a duração das solicitações do cliente e armazena os resultados em um histograma.

Para visualizar as métricas geradas pelo app de exemplo, faça o seguinte:
  1. No Console do Google Cloud, acesse a página do  Metrics Explorer:

    Acesse o Metrics explorer

    Se você usar a barra de pesquisa para encontrar essa página, selecione o resultado com o subtítulo Monitoramento.

  2. No elemento Metric, expanda o menu Selecionar uma métrica, digite http_server na barra de filtro e use os submenus para selecionar um tipo de recurso e métrica específicos:
    1. No menu Active resources, selecione Prometheus Target.
    2. No menu Categorias de métrica ativas, selecione Instância.
    3. No menu Métricas ativas, selecione uma métrica de faturamento†.
    4. Clique em Aplicar.
  3. Configure a visualização dos dados.

    Quando as medições de uma métrica são cumulativas, o Metrics Explorer normaliza automaticamente os dados medidos pelo período de alinhamento, o que resulta na exibição de uma taxa no gráfico. Para mais informações, consulte Tipos, tipos e conversões.

    Quando valores inteiros ou duplos são medidos, como acontece com as duas métricas counter, o Metrics Explorer soma automaticamente todas as séries temporais. Para visualizar os dados das rotas HTTP /multi e /single, defina o primeiro menu da entrada Agregação como Nenhum.

    Para mais informações sobre como configurar um gráfico, consulte Selecionar métricas ao usar o Metrics Explorer.

Visualizar os rastros

Para visualizar os dados de trace, faça o seguinte:

  1. No console do Google Cloud, acesse a página Explorador de traces:

    Acessar o Explorador de traces

    Também é possível encontrar essa página usando a barra de pesquisa.

  2. No gráfico de dispersão, selecione um rastro com o URI de /multi.
  3. No diagrama de Gantt no painel Detalhes do trace, selecione o período rotulado como /multi.

    Um painel é aberto com informações sobre a solicitação HTTP. Esses detalhes incluem o método, o código de status, o número de bytes e o user agent do autor da chamada.

  4. Para visualizar os registros associados a esse trace, selecione a guia Registros e eventos.

    A guia mostra registros individuais. Para exibir os detalhes da entrada de registro, expanda a entrada de registro. Também é possível clicar em Ver registros e ver o registro usando a Análise de registros.

Para mais informações sobre como usar o explorador do Cloud Trace, consulte Encontrar e explorar traces.

Acessar os registros

Na Análise de registros, é possível inspecionar os registros e visualizar os traces associados, quando eles existirem.

  1. No console do Google Cloud, acesse a página do Análise de registros.

    Acessar a Análise de registros

    Se você usar a barra de pesquisa para encontrar essa página, selecione o resultado com o subtítulo Monitoring.

  2. Localize um registro com a descrição de handle /multi request.

    Para ver os detalhes do registro, expanda a entrada.

  3. Clique em Traces em uma entrada de registro com a mensagem "handle /multi request" e selecione View trace details.

    O painel Detalhes do trace é aberto e mostra o trace selecionado.

Para mais informações sobre como usar a Análise de registros, consulte Ver registros usando a Análise de registros.

A seguir