"O sistema está lento." Onde você olha primeiro? Logs? Métricas? Traces? A resposta certa depende da pergunta que você está fazendo. Os três pilares da observabilidade são complementares, não concorrentes. Este artigo ensina quando usar cada um.
Observabilidade não é ter dados. É conseguir responder qualquer pergunta sobre seu sistema.
Os Três Pilares
Visão geral
Métricas:
O que: Números agregados ao longo do tempo
Responde: "O que está acontecendo?"
Exemplo: "p95 latência = 200ms"
Logs:
O que: Eventos discretos com contexto
Responde: "O que aconteceu especificamente?"
Exemplo: "Request X falhou com erro Y no momento Z"
Traces:
O que: Jornada de uma requisição através do sistema
Responde: "Por onde passou e quanto tempo levou?"
Exemplo: "Request passou por A (50ms) → B (200ms) → C (30ms)"
Analogia
Métricas = Termômetro
"Você está com febre de 39°C"
Sabe que algo está errado, mas não sabe o quê
Logs = Histórico médico
"Paciente relatou dor de garganta há 3 dias"
Eventos específicos que ajudam no diagnóstico
Traces = Exame de imagem
"Infecção localizada na amígdala direita"
Visualização completa do problema
Métricas
Características
Formato: Valores numéricos com timestamp
Granularidade: Agregada (médias, percentis, contagens)
Volume: Baixo (pontos de dados compactados)
Retenção: Longa (meses a anos)
Custo: Baixo por dado armazenado
Tipos de métricas
Counter:
- Sempre incrementa
- Ex: total de requests, erros, bytes
Gauge:
- Pode subir ou descer
- Ex: temperatura, conexões ativas, memória
Histogram:
- Distribuição de valores
- Ex: latência, tamanho de payload
Summary:
- Percentis pré-calculados
- Ex: p50, p95, p99 de latência
Exemplos em Prometheus
# Counter: Taxa de requests por segundo
rate(http_requests_total[5m])
# Gauge: Conexões ativas agora
db_connections_active
# Histogram: Percentil 95 de latência
histogram_quantile(0.95, rate(http_duration_seconds_bucket[5m]))
# Agregação: Error rate
sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m]))
Quando usar métricas
✅ Use para:
- Alertas (threshold violado)
- Dashboards de saúde
- Tendências ao longo do tempo
- Comparação de períodos
- Capacidade planning
❌ Não use para:
- Investigar request específico
- Entender contexto de erro
- Debug de caso individual
Logs
Características
Formato: Texto ou JSON com timestamp
Granularidade: Evento individual
Volume: Alto (cada evento é registrado)
Retenção: Média (dias a semanas)
Custo: Alto por volume
Níveis de log
DEBUG:
- Detalhes para desenvolvimento
- Nunca em produção
INFO:
- Eventos normais importantes
- "Usuário X fez login"
WARN:
- Situações incomuns mas não críticas
- "Retry necessário para serviço Y"
ERROR:
- Falhas que precisam atenção
- "Timeout conectando ao banco"
FATAL:
- Sistema não pode continuar
- "Configuração inválida, encerrando"
Logs estruturados
// ❌ Log não estruturado
"2024-01-15 10:30:45 ERROR Failed to process order 12345 for user john@example.com"
// ✅ Log estruturado
{
"timestamp": "2024-01-15T10:30:45Z",
"level": "ERROR",
"message": "Failed to process order",
"order_id": "12345",
"user_email": "john@example.com",
"error_type": "PaymentDeclined",
"payment_provider": "stripe",
"trace_id": "abc123",
"duration_ms": 1523
}
Quando usar logs
✅ Use para:
- Investigar erros específicos
- Auditoria e compliance
- Debug de fluxos de negócio
- Contexto de falhas
❌ Não use para:
- Métricas agregadas (use métricas)
- Visualizar fluxo de request (use traces)
- Alertas de threshold
Boas práticas de logging
1. Sempre estruturado:
- JSON com campos padronizados
- Facilita queries e análise
2. Inclua contexto:
- trace_id para correlação
- user_id para investigação
- request_id para rastreamento
3. Evite PII desnecessário:
- Não logue senhas, tokens
- Mascare dados sensíveis
4. Log no nível certo:
- Produção: INFO e acima
- Debug apenas quando necessário
Traces
Características
Formato: Spans conectados por trace_id
Granularidade: Por request, através de serviços
Volume: Médio (amostrado em alto tráfego)
Retenção: Curta (horas a dias)
Custo: Médio a alto
Anatomia de um trace
Trace ID: abc-123-xyz
├─ Span: API Gateway (0-50ms)
│ └─ Span: Auth Service (10-30ms)
│ └─ Span: Redis Cache (15-20ms)
│
├─ Span: Order Service (50-250ms)
│ ├─ Span: PostgreSQL Query (60-150ms)
│ └─ Span: Inventory Check (160-200ms)
│
└─ Span: Payment Service (250-400ms)
└─ Span: Stripe API (280-390ms)
Total: 400ms
Gargalo identificado: PostgreSQL Query (90ms)
Implementando tracing
// Exemplo com OpenTelemetry
const span = tracer.startSpan('process_order', {
attributes: {
'order.id': orderId,
'customer.id': customerId,
},
});
try {
// Span filho para operação de DB
const dbSpan = tracer.startSpan('db.query', { parent: span });
const order = await db.getOrder(orderId);
dbSpan.end();
// Span filho para pagamento
const paymentSpan = tracer.startSpan('payment.process', { parent: span });
await processPayment(order);
paymentSpan.end();
span.setStatus({ code: SpanStatusCode.OK });
} catch (error) {
span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
throw error;
} finally {
span.end();
}
Quando usar traces
✅ Use para:
- Identificar gargalos em requests lentos
- Entender fluxo entre microserviços
- Debug de latência distribuída
- Visualizar dependências
❌ Não use para:
- Alertas (muito granular)
- Tendências de longo prazo (use métricas)
- Auditoria detalhada (use logs)
Integrando os Três Pilares
Fluxo de investigação
1. ALERTA (métrica)
"Error rate subiu para 5%"
2. CONTEXTO (métrica)
"Aumento correlacionado com latência de DB"
3. INVESTIGAÇÃO (trace)
"Requests lentos passam pela query X"
4. DETALHE (log)
"Query X falhando com timeout por lock"
5. ROOT CAUSE
"Deploy Y introduziu lock contention"
Correlação por trace_id
O trace_id conecta tudo:
Métrica:
http_request_duration{trace_id="abc123"} = 2.5s
Trace:
trace_id: abc123
spans: [gateway, auth, order, payment]
duration: 2500ms
bottleneck: payment (2000ms)
Log:
{
"trace_id": "abc123",
"service": "payment",
"message": "Stripe timeout after 2000ms",
"error_code": "TIMEOUT"
}
Exemplo prático de investigação
## Cenário: "Sistema lento às 10h"
### 1. Métricas (Grafana)
- p95 latência: 2s (normal: 200ms)
- Throughput: normal
- Error rate: 3% (normal: 0.1%)
→ Problema confirmado, não é percepção
### 2. Drill-down em métricas
- Latência por endpoint: /api/checkout 10x mais lento
- Latência por serviço: Payment service degradado
→ Problema localizado em payment
### 3. Traces (Jaeger)
- Trace de request lento
- 90% do tempo em span "stripe_api_call"
→ Gargalo é chamada externa para Stripe
### 4. Logs (Elasticsearch)
```json
{
"timestamp": "2024-01-15T10:05:00Z",
"service": "payment",
"message": "Stripe API retry attempt 3",
"trace_id": "xyz789",
"response_time_ms": 1800,
"stripe_error": "rate_limited"
}
→ Root cause: Rate limiting do Stripe
5. Resolução
- Implementar circuit breaker
- Adicionar cache de validação
- Negociar rate limit com Stripe
## Ferramentas por Pilar
### Stack open source
```yaml
Métricas:
Coleta: Prometheus, Victoria Metrics
Visualização: Grafana
Alertas: Alertmanager
Logs:
Coleta: Fluentd, Fluent Bit, Vector
Storage: Elasticsearch, Loki
Visualização: Kibana, Grafana
Traces:
Coleta: OpenTelemetry, Jaeger Agent
Storage: Jaeger, Tempo, Zipkin
Visualização: Jaeger UI, Grafana
Stack gerenciada
All-in-one:
- Datadog
- New Relic
- Dynatrace
- Splunk
Por pilar:
Métricas: CloudWatch, Datadog
Logs: CloudWatch Logs, Papertrail
Traces: X-Ray, Honeycomb
Conclusão
Os três pilares são complementares:
- Métricas: Detectam que algo está errado (alertas)
- Traces: Mostram onde está o problema (localização)
- Logs: Explicam por que aconteceu (contexto)
Use os três juntos:
- Correlacione por trace_id
- Comece por métricas para visão geral
- Use traces para localizar
- Use logs para detalhar
Um sistema verdadeiramente observável permite responder perguntas que você não pensou em fazer antes de ter o problema.
Este artigo faz parte da série sobre a metodologia OCTOPUS de Performance Engineering.