Observabilidade: logs, métricas e traces
1. Fundamentos da Observabilidade em Sistemas Distribuídos
1.1. Definição e objetivos: monitoramento reativo vs. observabilidade preditiva
Observabilidade é a capacidade de inferir o estado interno de um sistema a partir de seus dados externos. Diferentemente do monitoramento reativo, que apenas dispara alertas quando métricas predefinidas são violadas, a observabilidade preditiva permite explorar comportamentos inesperados sem necessidade de instrumentação prévia para cada cenário.
Em sistemas distribuídos, onde falhas em cascata são comuns, a observabilidade não é um luxo — é um requisito arquitetural. Sem ela, uma requisição que atravessa 15 microsserviços pode falhar silenciosamente, deixando engenheiros sem pistas sobre a causa raiz.
1.2. Os três pilares: logs, métricas e traces – papéis e interdependências
Os três pilares da observabilidade atuam de forma complementar:
- Logs: registros textuais de eventos discretos. Contam o que aconteceu.
- Métricas: agregados numéricos ao longo do tempo. Mostram tendências e padrões.
- Traces: rastreamento de requisições através de serviços. Revelam caminhos e tempos de execução.
Individualmente, cada pilar é limitado. Juntos, formam um sistema coeso: métricas acionam alertas, logs fornecem contexto, e traces revelam a topologia da falha.
1.3. Contexto arquitetural: microserviços, eventos assíncronos e falhas em cadeia
Arquiteturas modernas introduzem complexidade não-linear. Um timeout em um serviço de pagamento pode ser causado por contenção em um banco de dados de catálogo, a três saltos de distância. A observabilidade precisa atravessar fronteiras de serviço, protocolos e times.
2. Logs: Registro Estruturado e Contextual
2.1. Logs estruturados (JSON) vs. logs não estruturados – impacto em consultas e correlação
Logs não estruturados são difíceis de consultar em escala. Compare:
2025-01-15 10:30:45 ERRO falha ao processar pedido 12345
Com um log estruturado:
{"timestamp":"2025-01-15T10:30:45Z","level":"ERROR","service":"payment","trace_id":"abc123","order_id":12345,"message":"falha ao processar pedido","error":"timeout"}
O formato estruturado permite consultas como service=payment AND level=ERROR e correlação direta com traces via trace_id.
2.2. Níveis de severidade e boas práticas: evitar ruído, usar IDs de correlação
Boas práticas incluem:
- Usar níveis padronizados: DEBUG, INFO, WARN, ERROR, FATAL
- Incluir sempre trace_id, service e timestamp
- Evitar logs em loops de alta frequência
- Logar em pontos de entrada/saída de serviços e em decisões críticas
2.3. Centralização de logs com agentes (Fluentd, Logstash) e armazenamento de longa duração
Agentes como Fluentd coletam logs localmente e os enviam para armazenamento centralizado (Elasticsearch, Loki). O pipeline típico:
Aplicação → Fluentd (buffer) → Kafka (fila) → Logstash (transformação) → Elasticsearch → Kibana
3. Métricas: Sinais Quantitativos de Saúde e Desempenho
3.1. Tipos de métricas: contadores, gauges, histogramas e sumários
- Contador: valor monotônico crescente (ex: requisições totais)
- Gauge: valor que sobe e desce (ex: memória usada)
- Histograma: distribuição de valores (ex: latência em buckets)
- Sumário: quantis calculados no cliente (ex: p99)
3.2. Métricas RED (Rate, Errors, Duration) e USE (Utilization, Saturation, Errors)
Dois frameworks consolidados:
RED (para serviços):
- Rate: requisições por segundo
- Errors: taxa de falhas
- Duration: latência (p50, p95, p99)
USE (para recursos):
- Utilization: percentual de uso
- Saturation: fila de espera
- Errors: contagem de falhas
3.3. Coleta e agregação: pull (Prometheus) vs. push (StatsD, Telegraf) – trade-offs arquiteturais
| Modelo | Vantagens | Desvantagens |
|---|---|---|
| Pull (Prometheus) | Descoberta automática, menos carga em picos | Requer configuração de rede, difícil em ambientes efêmeros |
| Push (StatsD) | Simples, funciona em qualquer topologia | Pode perder dados se coletor cair, requer autenticação |
4. Traces: Rastreamento Distribuído de Requisições
4.1. Conceitos: spans, trace context, parent-child relationships
Um trace representa uma requisição completa. Cada span é uma unidade de trabalho dentro do trace. Spans formam uma árvore com relações pai-filho.
Trace ID: abc123
├── Span A (frontend) — 200ms
│ ├── Span B (auth) — 50ms
│ └── Span C (payment) — 120ms
│ └── Span D (database) — 80ms
4.2. Propagação de contexto: headers W3C Trace Context e baggage
O padrão W3C Trace Context define headers traceparent e tracestate para propagar contexto entre serviços. Cada serviço extrai o contexto, cria um span filho e repassa adiante.
4.3. Amostragem (sampling) – head-based vs. tail-based, custo vs. cobertura
- Head-based: decide amostrar no início da requisição. Simples, mas pode perder eventos raros.
- Tail-based: coleta todos os spans, decide após análise. Mais preciso, porém mais caro.
Estratégia comum: amostrar 100% de erros e 1-5% de requisições bem-sucedidas.
5. Correlação entre os Três Pilares na Prática
5.1. Unificação via ID de correlação: trace ID em logs e métricas
A chave para correlação é propagar o trace_id em todos os sinais:
Log: {"trace_id":"abc123","service":"payment","level":"ERROR","message":"timeout"}
Métrica: http_requests_duration_seconds{trace_id="abc123",status="500"} 2.3
Trace: Span C (payment) — erro com duração 2.3s
5.2. Dashboards e alertas: métricas para alarmes, traces para diagnóstico, logs para detalhamento
Fluxo típico de debugging:
1. Alerta no Grafana: latência p99 > 2s
2. Abre trace correspondente no Jaeger: descobre que o gargalo está no serviço de pagamento
3. Consulta logs daquele trace_id: encontra "timeout connecting to database"
5.3. Exemplo prático: debugging de latência alta combinando os três sinais
Suponha que métricas mostrem aumento repentino no p95 de latência:
# Métrica (PromQL):
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
Ao inspecionar traces no Jaeger, identificamos que o serviço inventory está demorando. Logs desse trace revelam:
{"trace_id":"xyz789","service":"inventory","level":"WARN","message":"cache miss for product 5001","duration_ms":450}
Conclusão: o cache Redis estava expirado, causando consultas lentas ao banco.
6. Desafios Arquiteturais e Padrões de Implementação
6.1. Sobrecarga de instrumentação e impacto no desempenho
Instrumentação excessiva pode degradar performance. Soluções:
- Usar sampling inteligente
- Instrumentar assincronamente (bibliotecas não-bloqueantes)
- Separar pipeline de observabilidade do tráfego principal
6.2. Consistência eventual e ordenação de eventos em pipelines de observabilidade
Logs e traces chegam fora de ordem. Estratégias:
- Usar timestamps nanossegundos
- Implementar buffers com ordenação por partição (ex: Kafka por trace_id)
- Aceitar que a visualização pode ter atraso de segundos
6.3. Custos de armazenamento e estratégias de retenção (hot/warm/cold tiers)
| Tier | Armazenamento | Retenção | Exemplo |
|---|---|---|---|
| Hot | SSD, alta performance | 7 dias | Elasticsearch rápido |
| Warm | HDD, performance média | 30 dias | Elasticsearch com shards reduzidos |
| Cold | S3/Blob Storage | 1 ano | Dados compactados, consulta sob demanda |
7. Ferramentas e Ecossistema Open Source
7.1. OpenTelemetry como camada de instrumentação padronizada
OpenTelemetry (OTel) unifica a coleta de logs, métricas e traces em um único SDK. Suporta múltiplas linguagens e exporta para diversos backends.
# Configuração OTel (exemplo YAML):
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
exporters:
prometheus:
endpoint: 0.0.0.0:8889
jaeger:
endpoint: jaeger:14250
service:
pipelines:
traces:
receivers: [otlp]
exporters: [jaeger]
metrics:
receivers: [otlp]
exporters: [prometheus]
7.2. Stack comum: Prometheus + Grafana (métricas), Elasticsearch + Kibana (logs), Jaeger/Tempo (traces)
- Métricas: Prometheus coleta, Grafana visualiza e alerta
- Logs: Elasticsearch armazena, Kibana consulta
- Traces: Jaeger ou Grafana Tempo para rastreamento distribuído
7.3. Integração com ADRs: decisões arquiteturais sobre escolha de ferramentas e formatos
Documentar decisões como ADRs (Architecture Decision Records) é crucial:
ADR-007: Adoção do OpenTelemetry
Contexto: Times usavam SDKs diferentes (Zipkin, OpenTracing, OpenCensus)
Decisão: Padronizar em OpenTelemetry para logs, métricas e traces
Consequências: Migração de 3 meses, redução de custos de manutenção em 40%
Referências
- OpenTelemetry Documentation — Documentação oficial do padrão de instrumentação unificada para logs, métricas e traces
- Prometheus Documentation - Metrics Types — Guia completo sobre tipos de métricas (counter, gauge, histogram, summary) e boas práticas
- W3C Trace Context Specification — Especificação oficial para propagação de contexto de tracing distribuído
- Grafana Labs - Observability Concepts — Conceitos fundamentais de observabilidade com exemplos práticos de dashboards
- Jaeger Documentation - Sampling — Estratégias de amostragem head-based e tail-based para tracing distribuído
- The RED Method - Tom Wilkie — Artigo seminal sobre métricas RED (Rate, Errors, Duration) para microsserviços
- Elastic Common Schema (ECS) — Padrão para estruturação de logs que facilita correlação com traces e métricas