DEV Community

RaVi
RaVi

Posted on

Distributed Tracing com plugins do OpenTelemetry, Node e Jaeger

No dia 30 de março foi anunciada a fase beta do OpenTelemetry. O objetivo do projeto é facilitar o rastreamento distribuído através de uma série de ferramentas integradas como descrito no meu primeiro artigo sobre o assunto. Entre as metas estabelecidas para o lançamento do beta estava a integração de plugins que permitem instrumentação automática das aplicações, diminuindo de forma significativa o trabalho necessário para implementação. Os repositórios das bibliotecas já funcionais e em desenvolvimento podem ser encontrados na página do projeto no github.

Esse artigo trás um exemplo de Distributed Tracing de aplicações Node usando http, Express e IORedis para simular o monitoramento de uma arquitetura de micro serviços usando a API javascript do OpenTelemetry e os plugins de instrumentação automática de cada módulo. Vamos explorar brevemente pedaços de código para ilustrar o funcionamento do sistema e o que é necessário ser feito para ter as informações de execução disponíveis no Jaeger como na imagem:

Imgur



Visualização dos spans de um trace na interface do Jaeger

Nossa implementação de exemplo consiste em dois servidores, o primeiro simula uma API que serve de primeiro ponto de contato na comunicação e repassa as requisições para o segundo, que é responsável por buscar os dados necessários acessando o redis. Recomendo muito dar uma olhada no código completo aqui e para rodar basta fazer docker-compose up ou seguir as instruções para cada serviço. No compose temos os containers de cada serviço e o Jaeger que vai servir como backend e interface para análise dos spans coletados.

Como uma aplicação de exemplo, o funcionamento é bem simplificado. Ao acessar localhost:3000 pela primeira vez um resultado vazio é retornado.

Imgur

Essa rota acessa o middleware e pede os valores para as keys key1, key2 e key3.

// api.js
...
const values = ['key1', 'key2', 'key3']

app.get('/', async (_, res) => {
  console.log('get /', middleware_url);
  const resultList = await Promise.all(
    values.map(v => httpRequest(middleware_url, 4000, '/' + v))
  )

  console.log({ resultList });
  res.status(200).send(resultList.toString());
})
...
Enter fullscreen mode Exit fullscreen mode

Usamos a rota /set-values para popular esses valores:

// api.js
...
  app.get('/set-values', async (_, res) => {
    const testItens = [
      { key: 'key1', value: 'value1' },
      { key: 'key2', value: 'value2' },
      { key: 'key3', value: 'value3' },
    ]

    await Promise.all(
      testItens.map(i => httpRequest(middleware_url, 4000, `/set?key=${i.key}&value=${i.value}`))
    ).then((result) => {
      res.status(200).send(result.toString());
    })
  })
...
Enter fullscreen mode Exit fullscreen mode

e agora ao acessar localhost:3000 obtemos os valores para as chaves solicitadas:

Imgur

Se você rodou o código a partir do repositório completo e fez esses 3 passos já pode visualizar os traces gerados na interface do Jaeger em: http://localhost:16686 selecionando o Service api e clicando em find traces.

Imgur

Isso é possível graças aos plugins do OpenTelemetry. Com a instrumentação automática não é preciso mudar cada parte do código para adicionar o tracing. Os plugins fazem patch dos módulos e escutam as chamadas internas adicionando a passagem de contexto onde é necessário.

No topo dos arquivos api.js e middleware.js você verá:

const { createTracer } = require('./tracer');
createTracer('api');
Enter fullscreen mode Exit fullscreen mode

O tracer importa as bibliotecas do OpenTelemetry e inicializa o NodeTracerProvider que precisa ser definido antes do import dos módulos suportados. Aqui também é configurado o exporter do Jaeger.

// tracer.js
const opentelemetry = require('@opentelemetry/api');
const { NodeTracerProvider } = require('@opentelemetry/node');
const { JaegerExporter } = require('@opentelemetry/exporter-jaeger');
const { SimpleSpanProcessor } = require('@opentelemetry/tracing');

const createTracer = (serviceName) => {
  const provider = new NodeTracerProvider();
  exporter = new JaegerExporter({
    serviceName,
    host: 'jaeger'
  });
  provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
  provider.register();

  return opentelemetry.trace.getTracer(serviceName)
}


module.exports = {
  createTracer,
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)