Distributed tracing: entendendo o caminho de uma requisição em microsserviços
1. O Desafio da Rastreabilidade em Arquiteturas Distribuídas
1.1. Por que logs e métricas isolados são insuficientes em microsserviços
Em sistemas monolíticos, rastrear uma requisição é trivial: um único processo, um único log, uma única métrica. Em arquiteturas de microsserviços, uma única requisição pode atravessar dezenas de serviços independentes, cada um com seus próprios logs e métricas. Logs isolados mostram o que aconteceu em cada serviço, mas não revelam a ordem exata dos eventos, o tempo gasto em cada etapa ou onde exatamente uma falha ocorreu. Métricas agregadas, como latência média ou taxa de erro, escondem os outliers — justamente os problemas mais críticos.
1.2. O problema da latência oculta e falhas em cascata
Imagine um serviço de catálogo que chama um serviço de estoque, que por sua vez chama um serviço de preços. Se a requisição total leva 5 segundos, onde está o gargalo? Sem tracing, você precisa adivinhar ou inserir logs manuais em cada ponto. Pior ainda: uma falha no serviço de preços pode causar timeouts em cascata, derrubando todo o sistema. Logs e métricas isolados mostram os sintomas, mas não a causa raiz.
1.3. Conceito fundamental: contexto de propagação entre serviços
A solução é propagar um contexto único (trace) através de todas as chamadas entre serviços. Esse contexto carrega identificadores que permitem reconstruir o caminho completo da requisição, como um fio de Ariadne digital.
2. Fundamentos do Distributed Tracing
2.1. Estrutura de um trace: spans, trace ID e span ID
Um trace representa o percurso completo de uma requisição. Dentro dele, cada operação unitária é um span. Cada span possui:
- trace ID: identifica o trace global
- span ID: identifica o span individual
- parent span ID: identifica o span que o chamou
2.2. Hierarquia de spans: pai-filho e árvore de chamadas
Os spans formam uma árvore hierárquica. O span raiz representa a requisição inicial. Cada chamada subsequente cria um span filho. Essa hierarquia permite visualizar exatamente a sequência e o aninhamento das operações.
Exemplo de hierarquia de spans:
Trace ID: abc123
├── Span A (raiz): "GET /api/pedidos" (duração: 500ms)
│ ├── Span B: "consultar_estoque" (duração: 200ms)
│ │ └── Span C: "consultar_banco_estoque" (duração: 150ms)
│ └── Span D: "calcular_frete" (duração: 250ms)
│ └── Span E: "chamar_api_frete" (duração: 200ms)
2.3. Metadados essenciais: timestamps, duração, tags e baggage
Cada span carrega metadados cruciais:
- timestamp: momento de início
- duração: tempo total da operação
- tags: pares chave-valor para contexto (ex: http.method, error.code)
- baggage: dados propagados entre spans para contexto de negócio (ex: user.id)
3. Propagação de Contexto: O Coração do Tracing
3.1. Propagação via headers HTTP (W3C Trace Context e Zipkin B3)
A propagação mais comum ocorre via headers HTTP. Dois padrões se destacam:
W3C Trace Context (recomendado):
traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
tracestate: vendor=eyJzZWNyZXQiOiJ0cnVlIn0=
Zipkin B3 (legado):
x-b3-traceid: 0af7651916cd43dd8448eb211c80319c
x-b3-spanid: b7ad6b7169203331
x-b3-parentspanid: 0020000000000001
x-b3-sampled: 1
3.2. Propagação em filas, mensageria e eventos assíncronos
Em sistemas assíncronos, o contexto precisa ser propagado nas mensagens. Exemplo com RabbitMQ:
Headers da mensagem:
traceparent: 00-abc123...-def456...-01
tracestate: ...
3.3. Desafios de propagação em chamadas gRPC e GraphQL
gRPC usa metadados de contexto nativos. GraphQL requer propagação nos resolvers e data loaders. Ambos exigem instrumentação específica para manter o trace completo.
4. Instrumentação: Como Coletar os Dados
4.1. Instrumentação manual vs. automática (bibliotecas e agentes)
- Automática: agentes como OpenTelemetry auto-instrumentation interceptam chamadas HTTP, banco de dados e mensageria sem alterar código
- Manual: criação explícita de spans para lógica de negócio personalizada
4.2. OpenTelemetry como padrão unificado de coleta
OpenTelemetry é o padrão da CNCF para coleta de traces, métricas e logs. Oferece SDKs para todas as linguagens principais.
4.3. Exemplo de código: criação manual de spans com OpenTelemetry
# Python com OpenTelemetry
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor
# Configuração
provider = TracerProvider()
provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))
trace.set_tracer_provider(provider)
tracer = trace.get_tracer(__name__)
# Criação manual de spans
def processar_pedido(pedido_id):
with tracer.start_as_current_span("processar_pedido") as span:
span.set_attribute("pedido.id", pedido_id)
span.set_attribute("pedido.valor", 150.00)
# Lógica do serviço
resultado = realizar_pagamento(pedido_id)
if resultado["status"] == "erro":
span.set_status(trace.Status(trace.StatusCode.ERROR))
span.record_exception(resultado["erro"])
span.add_event("pedido.processado", {"duracao_ms": 45})
return resultado
5. Armazenamento e Consulta de Traces
5.1. Backends populares: Jaeger, Zipkin, Grafana Tempo, Datadog
| Ferramenta | Tipo | Diferencial |
|---|---|---|
| Jaeger | Open Source (CNCF) | Interface rica, suporte a sampling |
| Zipkin | Open Source | Simplicidade, legado |
| Grafana Tempo | Open Source | Integração com Grafana, baixo custo |
| Datadog APM | SaaS | Correlação nativa com logs e métricas |
5.2. Amostragem: head-based vs. tail-based sampling
- Head-based: decide amostrar no início do trace (ex: 1 em cada 100 requisições)
- Tail-based: amostra baseada em características do trace completo (ex: traces com erro ou alta latência)
5.3. Consultas por trace ID, tags e filtros temporais
# Exemplo de consulta no Jaeger
find traces with:
service = "servico-pedidos"
tags = {"error": "true"}
duration > 1000ms
time range = "última 1 hora"
6. Análise Prática: Diagnosticando Problemas com Traces
6.1. Identificando gargalos de latência em cadeias de chamadas
Um trace revela exatamente qual span consumiu mais tempo. Se o span "consultar_banco" leva 800ms de um total de 1s, o gargalo está no banco de dados.
6.2. Detectando erros e exceções distribuídas
Traces com status ERROR e tags como http.status_code=500 indicam falhas. A árvore de spans mostra exatamente onde o erro ocorreu e qual serviço foi impactado.
6.3. Correlação com logs e métricas (observabilidade integrada)
Com OpenTelemetry, logs podem carregar trace_id e span_id, permitindo consultar logs específicos de um trace problemático. Métricas de latência podem ser segmentadas por serviço e operação.
7. Boas Práticas e Armadilhas Comuns
7.1. Cuidados com overhead de instrumentação e volume de dados
Cada span adiciona latência (geralmente < 1ms) e gera dados. Em sistemas com milhões de requisições por segundo, o volume de traces pode ser inviável. Use sampling agressivo (ex: 1%) e evite spans desnecessários.
7.2. Políticas de retenção e custos de armazenamento
Traces ocupam espaço. Defina:
- Retenção de 7 dias para traces completos
- Retenção de 30 dias para traces com erro
- Agregação de métricas para dados históricos
7.3. Integração com service mesh (Istio, Linkerd) para tracing transparente
Service meshes como Istio podem gerar spans automaticamente para todo o tráfego entre serviços, sem instrumentação de código. Isso fornece tracing básico imediato, embora sem contexto de negócio.
8. Do Tracing à Resiliência: Próximos Passos
8.1. Uso de traces para testes de caos e engenharia de resiliência
Traces permitem simular falhas controladas e observar o impacto real no sistema. Ferramentas como Chaos Mesh usam traces para validar que circuit breakers e retries funcionam corretamente.
8.2. SLIs e SLOs baseados em dados de tracing
Com traces, você pode calcular SLIs precisos como:
- Latência p95 por serviço e operação
- Taxa de erro por endpoint
- Disponibilidade baseada em traces completos bem-sucedidos
8.3. Evolução para rastreamento em tempo real e alertas proativos
Ferramentas modernas permitem alertas em tempo real baseados em traces: "Se latência p99 do serviço X ultrapassar 2s por mais de 5 minutos, notificar equipe". Isso transforma tracing de ferramenta de diagnóstico em sistema de prevenção.
Referências
- OpenTelemetry Documentation — Documentação oficial do padrão unificado de observabilidade, incluindo guias de instrumentação para todas as linguagens
- W3C Trace Context Specification — Especificação oficial do padrão de propagação de contexto de tracing via HTTP
- Jaeger Documentation — Documentação oficial do Jaeger, backend open source para armazenamento e consulta de traces
- Grafana Tempo Documentation — Documentação do Grafana Tempo, backend de tracing de alta escalabilidade integrado ao ecossistema Grafana
- Distributed Tracing in Microservices with OpenTelemetry (CNCF) — Artigo técnico da CNCF sobre implementação prática de distributed tracing com OpenTelemetry
- Zipkin Documentation — Documentação oficial do Zipkin, sistema de tracing distribuído open source
- Istio Distributed Tracing — Guia oficial de configuração de tracing em service mesh Istio