DEV Community

RaVi
RaVi

Posted on

1

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)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up