02 - Logs Centralizados (ELK, Loki, CloudWatch Logs)
video: https://youtu.be/... no frontmatter da nota.Logs Centralizados (ELK, Loki, CloudWatch Logs)
Logs centralizados são o registro detalhado e ordenado de tudo que aconteceu em um sistema, coletado de todas as instâncias e serviços para um destino único, pesquisável e retido por política. Em produção, a centralização é obrigatória porque aplicação em múltiplos contêineres efêmeros torna o acesso a arquivos locais impraticável: o pod morre e leva o histórico junto. A stack de observabilidade de referência open source combina Loki para logs e Prometheus para métricas, enquanto o conjunto ELK, formado por Elasticsearch, Logstash e Kibana, segue como alternativa consolidada para busca full-text pesada, e o CloudWatch Logs cobre cargas ancoradas na AWS. Logs são o lado mais caro da observabilidade por volume, por isso decisões de formato, coleta, indexação e retenção têm impacto direto no custo mensal. Um pipeline bem desenhado transforma logs em ferramenta de investigação rápida; um pipeline mal desenhado transforma em desperdício silencioso.
O problema que a centralização resolve
A busca distribuída por arquivos de log em dezenas de instâncias, via SSH, é lenta, não escalável e não deixa trilha de auditoria de quem acessou o quê. Correlacionar um erro que atravessa três serviços exige contexto compartilhado, como um identificador de correlação, que só funciona se os logs estiverem agregados em um lugar único. A centralização também habilita casos de uso que não existem com arquivos espalhados: alertas baseados em padrões de log, detecção de anomalias, auditoria de acesso e análise de custo por time. Por fim, requisitos regulatórios de retenção e de proteção de dados pessoais precisam de um ponto único de política, em vez de regras manuais em cada servidor.
Logs estruturados e correlation id
O formato estruturado é a decisão de maior impacto em todo o pipeline. Em vez de texto livre que exige parsing frágil, cada linha é um documento JSON com campos previsíveis, o que permite filtrar, agregar e indexar sem expressões regulares. O identificador de correlação, normalmente o trace id da requisição, é o campo que conecta todas as entradas de um mesmo fluxo entre serviços diferentes. O exemplo abaixo mostra o padrão recomendado de linha de log.
{
"timestamp": "2025-05-12T14:03:22.481Z",
"level": "ERROR",
"service": "checkout-api",
"version": "2.14.1",
"environment": "production",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"span_id": "00f067aa0ba902b7",
"correlation_id": "ped-88213",
"logger": "PagamentoService",
"message": "Falha ao autorizar pagamento no provedor",
"error_type": "PaymentGatewayTimeout",
"order_id": "ped-88213",
"latency_ms": 3200
}
Três regras sustentam esse formato: o trace id deve ser propagado de ponta a ponta e impresso em todas as linhas do fluxo; campos sensíveis, como tokens e dados pessoais, devem ser mascarados na biblioteca de logging, nunca no pipeline, onde o vazamento já aconteceu; e exceções devem ser serializadas com stack trace em campo próprio, evitando quebras de linha que fragmentam o documento.
Pipeline de coleta e indexação
O pipeline típico tem quatro estágios: geração na aplicação, coleta por um agente, processamento e entrega ao armazenamento, e indexação para consulta. O agente roda ao lado da aplicação, como sidecar no pod ou DaemonSet no nó, lê stdout e stderr dos contêineres, aplica rotação, compressão e envio em lote. No caminho ELK, o Logstash ou o Fluente Bit processam os eventos, aplicam parsing e roteamento, e o Elasticsearch indexa cada campo em índices invertidos, o que torna qualquer busca muito rápida e o armazenamento caro. No caminho Loki, apenas os labels são indexados, como serviço, ambiente e nível, e o conteúdo completo é comprimido em chunks de objeto, consultado apenas quando a consulta já seleciona os labels. Essa é a diferença arquitetural central entre as duas abordagens e explica a diferença de custo.
# Configuração do Promtail (agente do Loki) para coletar logs de pods
scrape_configs:
- job_name: kubernetes-pods
kubernetes_sd_configs:
- role: pod
relabel_configs:
# Promove metadados do pod a labels indexados do Loki
- source_labels: [__meta_kubernetes_namespace]
target_label: namespace
- source_labels: [__meta_kubernetes_pod_label_app]
target_label: app
pipeline_stages:
# Faz o parsing do corpo JSON da linha de log
- json:
expressions:
level: level
trace_id: trace_id
# Exceções de runtime viram labels de baixa cardinalidade
- labels:
level:
A regra de ouro do Loki é indexar somente labels de cardinalidade baixa e finita, porque cada combinação de labels cria um fluxo separado. Colocar trace id como label quebra o modelo e degrada a performance; o caminho correto é filtrar por labels e usar filtros de linha sobre o conteúdo.
ELK versus Loki versus CloudWatch Logs
As três plataformas resolvem o mesmo problema com compromissos diferentes de custo, busca e operação. A escolha depende do perfil de consulta do time: busca livre em qualquer campo, ou consultas ancoradas em labels de serviço.
| Aspecto | ELK (Elasticsearch) | Loki | CloudWatch Logs |
|---|---|---|---|
| Modelo de índice | Índice invertido de todos os campos | Apenas labels; corpo comprimido | Índice por log group e timestamps |
| Custo por volume | Alto, armazenamento e memória caros | Baixo, chunks comprimidos em objeto | Médio, por GB ingerido e armazenado |
| Consulta | Lucene, KQL, busca full-text poderosa | LogQL com métricas sobre logs | CloudWatch Logs Insights |
| Operação | Complexa, cluster, shards, JVM tuning | Simples, peça única sem cluster pesado | Nula, serviço gerenciado |
| Alertas | Watchers, integrado ao stack | Suporte nativo via rules | Alarmes de métrica de log |
| Cenário ideal | Busca livre pesada, segurança e auditoria | Times Kubernetes com custo controlado | Cargas AWS sem equipe de plataforma |
Uma combinação comum em arquiteturas mistas usa Loki para logs operacionais de aplicação, com retenção curta e consulta por trace id, e Elasticsearch dedicado a domínios que exigem busca semântica, como segurança da informação e logs de acesso HTTP.
Consulta na prática
A consulta é o que transforma o arquivo centralizado em ferramenta de investigação, e cada plataforma tem sua linguagem. O padrão de investigação é o mesmo: filtrar por serviço e janela de tempo, reduzir por erro e saltar para o trace id relacionado. Os exemplos abaixo mostram o mesmo objetivo nas duas linguagens principais.
# Loki: todas as linhas de erro do checkout na última hora
{app="checkout-api", level="ERROR"} |= "PaymentGatewayTimeout"
# Loki: taxa de erros por minuto, calculada sobre o conteúdo dos logs
sum(rate({app="checkout-api", level="ERROR"}[5m])) by (level)
# Loki: listar trace ids distintos que falharam, base do salto para o tracing
{app="checkout-api", level="ERROR"} | json | trace_id != "" | line_format "{{ .trace_id }}"
-- CloudWatch Logs Insights: distribuição de erros por tipo na última hora
fields @timestamp, error_type, trace_id
| filter level = "ERROR"
| stats count() as falhas by error_type
| sort falhas desc
| limit 20
A consulta por trace id é a ponte mais valiosa do ecossistema de observabilidade, porque conecta a linha de log com o span correspondente no tracing. Times maduros linkam automaticamente essas consultas nos painéis, com o trace id como variável de template.
Retenção e custo
Retenção é decisão econômica, não técnica. Cada dia retido multiplica a fatura, por isso o padrão maduro é em camadas: dados quentes de 7 a 30 dias em índice de busca rápida, dados frios de 90 dias em armazenamento comprimido de custo reduzido, e arquivos de longo prazo, quando exigidos por regulação, em objetos quase imutáveis como S3 Glacier. O CloudWatch permite exportar para S3 automaticamente, e o Loki mantém chunks antigos em storage de objeto nativamente. Reduzir volume na origem rende mais do que negociar preço no destino: descartar logs de debug em produção, amestrar logs de sucesso de rotinas repetitivas e filtrar eventos redundantes no agente corta a maior parte do custo sem perda de capacidade de investigação.
Níveis de log e disciplina de escrita
Níveis existem para permitir filtragem progressiva, e a disciplina de uso é o que mantém os níveis úteis.
- ERROR deve significar intervenção humana necessária ou falha de um fluxo de negócio, e nunca erro esperado de validação, que é WARNING.
- WARNING registra degradação e situações anormais recuperadas, como retry bem-sucedido após timeout, e merece revisão periódica por padrões recorrentes.
- INFO conta eventos de negócio relevantes, como pedido criado e pagamento confirmado, em volume controlado, e é o nível padrão em produção.
- DEBUG detalha o fluxo interno e deve ser ativável em produção por amostragem ou por flag dinâmica, sem necessidade de redeploy.
O anti-padrão mais custoso é registrar a mesma informação em vários níveis ou logar objetos inteiros com dados pessoais; o objetivo é que cada linha responda uma pergunta específica de investigação.
Erros comuns
Os deslizes abaixo concentram a maior parte dos problemas observados em pipelines de log reais.
- Logs não estruturados em texto livre, que forçam parsing frágil no agente e impedem agregação eficiente por campo.
- Ausência de trace id nas linhas, o que torna impossível seguir um fluxo entre serviços e obriga investigação manual por timestamp.
- Dados sensíveis em log, como senhas, cartões e CPF, criando passivo regulatório que nenhum índice caro consegue compensar.
- Índices criados para todos os campos sem necessidade, gerando custo ELK desproporcional ao valor analítico entregue.
- Nível ERROR usado para erros esperados, treinando o time a ignorar alertas e escondendo os erros verdadeiros.
Boas práticas
As práticas a seguir consolidam um pipeline sustentável do ponto de vista técnico e financeiro.
- Padronize o esquema JSON em uma biblioteca interna de logging, com trace id, nome do serviço, versão e ambiente obrigatórios em toda linha.
- Envie stdout para o agente de coleta e trate arquivos locais como fallback de emergência, nunca como destino primário em contêineres.
- Defina política de retenção em camadas com custo revisitado trimestralmente, incluindo descarte de eventos sem consumo comprovado.
- Use o trace id do OpenTelemetry como correlation id, garantindo que logs, métricas e traces apontem para o mesmo fluxo.
- Monitore o próprio pipeline de log, com alertas de atraso de ingestão e de descarte de eventos, porque pipeline cego é blackout na investigação.