Tracing Distribuído

Spans, traces, OpenTelemetry, Jaeger e como acompanhar requests através de múltiplos serviços.

O que é o tracing distribuído?

Quando um request do usuário atravessa 5, 10 ou 20 serviços antes de devolver uma resposta, como saber onde o tempo foi gasto? Qual serviço falhou? Qual chamada foi a mais lenta?

O tracing distribuído resolve exatamente esse problema: permite acompanhar o percurso completo de um request através de todos os serviços que participam dele, medindo tempos e capturando contexto em cada etapa.

Conceitos fundamentais

Trace

Um trace representa o percurso completo de uma operação end-to-end. Por exemplo, um trace de “criar pedido” inclui tudo, desde quando o frontend envia o request até quando o usuário recebe a confirmação.

Cada trace tem um identificador único (traceId) que se propaga entre todos os serviços envolvidos.

Span

Um span representa uma unidade de trabalho dentro de um trace. Cada serviço ou componente que participa da operação cria um ou mais spans.

Um span contém:

  • Nome: Descrição da operação (ex: “POST /api/orders”).
  • Timestamps: Início e fim da operação.
  • SpanId: Identificador único do span.
  • ParentSpanId: Referência ao span pai, formando uma árvore.
  • Tags/Attributes: Metadados adicionais (método HTTP, status code, etc.).
  • Events/Logs: Eventos pontuais dentro do span.
  • Status: OK, ERROR ou UNSET.

Contexto de propagação

Para que o tracing funcione entre serviços, o contexto (traceId, spanId) precisa se propagar em cada chamada. Os mecanismos mais comuns são:

  • Headers HTTP: traceparent, tracestate (padrão W3C Trace Context).
  • Headers de mensageria: Atributos em mensagens do Kafka, RabbitMQ, SQS.
  • gRPC metadata: Propagação automática em chamadas gRPC.

OpenTelemetry

OpenTelemetry (OTel) é o padrão aberto para instrumentação de observabilidade. Unifica a coleta de traces, métricas e logs sob uma única API.

Por que OpenTelemetry?

Antes do OTel, cada vendor tinha sua própria API de instrumentação. Se você usava Jaeger, instrumentava com a API do Jaeger. Se mudava para Zipkin, tinha que reinstrumentar tudo. O OpenTelemetry resolve isso com uma API vendor-neutral.

Componentes do OTel

  1. API: Interface para instrumentar código. É estável e não muda.
  2. SDK: Implementação da API com configuração de exportadores, samplers, etc.
  3. Exporters: Enviam dados para backends específicos (Jaeger, Zipkin, OTLP).
  4. Collector: Componente intermediário que recebe, processa e exporta telemetria.
  5. Auto-instrumentação: Bibliotecas que instrumentam frameworks populares automaticamente.

O Collector

O OTel Collector é um componente chave em arquiteturas de produção:

Serviços → OTel Collector → Backend (Jaeger, Tempo, etc.)

Vantagens de usar um Collector:

  • Desacoplamento: Os serviços não precisam saber para qual backend enviar dados.
  • Processamento: Pode filtrar, samplear e enriquecer traces antes de exportá-los.
  • Buffering: Absorve picos de tráfego sem perder dados.
  • Multi-backend: Pode enviar dados para múltiplos destinos simultaneamente.

Jaeger

Jaeger é uma plataforma de tracing distribuído open-source, originalmente desenvolvida pela Uber.

Funcionalidades principais

  • Visualização de traces: Interface web para explorar traces como diagramas de cascata (waterfall).
  • Busca: Encontrar traces por serviço, operação, tags, duração ou traceId.
  • Comparação: Comparar dois traces para identificar diferenças de desempenho.
  • Grafos de dependência: Visualizar as relações entre serviços com base nos traces coletados.

Arquitetura do Jaeger

Jaeger pode ser implantado de várias formas:

  • All-in-one: Um único binário para desenvolvimento e testing.
  • Produção: Collector, armazenamento (Elasticsearch, Cassandra) e serviço de Query separados.
  • Com OTel Collector: Usando o Collector como receptor no lugar do agente do Jaeger.

Alternativas ao Jaeger

  • Zipkin: Outra plataforma open-source de tracing, mais simples.
  • Grafana Tempo: Backend de traces otimizado para custo, integrado ao Grafana.
  • AWS X-Ray: Serviço gerenciado de tracing na AWS.
  • Datadog APM: Plataforma SaaS com tracing integrado.

Correlação entre sinais

O verdadeiro poder do tracing aparece quando ele é correlacionado com logs e métricas:

Trace → Logs

Inclua o traceId em cada linha de log. Quando você encontrar um trace lento ou com erros, pode buscar todos os logs associados filtrando por esse traceId.

Trace → Métricas

Os traces podem gerar métricas automaticamente:

  • Latência por serviço e operação.
  • Taxa de erros por endpoint.
  • Distribuição de duração dos spans.

Isso é conhecido como “métricas derivadas de traces” e ferramentas como o Grafana Tempo suportam isso nativamente.

Exemplars

Os exemplars são links de uma métrica para um trace específico. Quando você vê um pico de latência em um dashboard, pode clicar e ir diretamente ao trace que o causou.

Sampling

Em sistemas de alto tráfego, capturar 100% dos traces pode ser proibitivamente custoso. O sampling permite capturar apenas um subconjunto representativo.

Tipos de sampling

  • Head-based sampling: Decide-se no início do trace se ele será capturado ou não. Simples, mas pode perder traces interessantes.
  • Tail-based sampling: Decide-se no final do trace, quando o resultado já é conhecido. Permite capturar 100% dos erros e dos traces lentos, mas requer buffering.
  • Rate limiting: Capturar um máximo de N traces por segundo.

Estratégia recomendada

  • Capture 100% dos erros — sempre.
  • Capture 100% dos traces lentos (acima de um limiar).
  • Aplique sampling probabilístico ao restante (ex: 10% dos traces bem-sucedidos).

Boas práticas

Instrumentação significativa

Não instrumente cada função — instrumente as operações que cruzam limites:

  • Chamadas HTTP/gRPC entre serviços.
  • Queries a bancos de dados.
  • Publicação/consumo de mensagens.
  • Chamadas a serviços externos.

Nomes de spans descritivos

Use nomes que identifiquem a operação, não a implementação:

  • Bom: POST /api/orders, OrderRepository.save
  • Ruim: handler, function1, doStuff

Atributos úteis

Adicione atributos que ajudem a diagnosticar problemas:

  • http.method, http.status_code, http.url
  • db.system, db.statement (sem dados sensíveis)
  • user.id, order.id (identificadores de negócio)

Propagação consistente

Garanta que o contexto se propague em todos os canais de comunicação — HTTP, mensageria, cron jobs, workers assíncronos. Um trace incompleto é pior do que não ter trace nenhum.

Resumo

O tracing distribuído é o terceiro pilar da observabilidade e possivelmente o mais valioso em arquiteturas de microsserviços. Permite entender o fluxo completo de uma operação, identificar gargalos e diagnosticar erros que envolvem múltiplos serviços. Com o OpenTelemetry como padrão e ferramentas como o Jaeger, implementar tracing é mais acessível do que nunca.