Logging estruturado em JSON: facilitando análise em produção
1. Por que logging estruturado é essencial em produção
Logs textuais tradicionais — como 2025-01-15 10:30:45 ERROR usuário não encontrado — são frágeis e difíceis de analisar em escala. Depender de grep e expressões regulares para extrair informações de logs não estruturados é ineficiente e propenso a erros. Cada linha exige parsing customizado, e metadados importantes (como ID da requisição, tempo de resposta ou ambiente) ficam perdidos ou exigem padrões de formatação inconsistentes.
O logging estruturado em JSON resolve esses problemas ao padronizar a saída. Cada entrada de log torna-se um objeto JSON legível por máquinas, permitindo consultas programáticas, correlação entre sistemas e integração direta com ferramentas de observabilidade. Em produção, onde milhares de logs são gerados por segundo, essa estruturação é a diferença entre conseguir ou não diagnosticar um incidente rapidamente.
2. Estrutura básica de um log em JSON
Um log JSON bem formado deve conter campos obrigatórios que garantam rastreabilidade mínima:
{
"timestamp": "2025-01-15T10:30:45.123Z",
"level": "ERROR",
"message": "Usuário não encontrado",
"service": "auth-service",
"version": "1.2.3"
}
Campos enriquecidos adicionam contexto valioso para análise:
{
"timestamp": "2025-01-15T10:30:45.123Z",
"level": "WARN",
"message": "Timeout na chamada externa",
"service": "payment-service",
"request_id": "req-abc123",
"user_id": "usr-456",
"duration_ms": 5023,
"environment": "production",
"region": "us-east-1"
}
Boas práticas de nomenclatura incluem o uso consistente de snake_case (adotado por ferramentas como Elasticsearch) ou camelCase (comum em Node.js). O importante é manter a padronização em toda a organização.
3. Implementação prática em diferentes linguagens
Python com python-json-logger
import logging
from pythonjsonlogger import jsonlogger
logger = logging.getLogger()
handler = logging.StreamHandler()
formatter = jsonlogger.JsonFormatter(
fmt='%(timestamp)s %(level)s %(name)s %(message)s %(request_id)s'
)
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.info("Pedido criado", extra={"request_id": "req-789", "amount": 150.00})
Node.js com pino
const pino = require('pino');
const logger = pino({
level: 'info',
base: { service: 'api-gateway' },
timestamp: pino.stdTimeFunctions.isoTime
});
logger.info({ requestId: 'req-abc', userId: 'usr-123' }, 'Login realizado');
Go com zap
import "go.uber.org/zap"
logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info("Conexão estabelecida",
zap.String("service", "database"),
zap.Int("port", 5432),
zap.Duration("latency", 2*time.Second),
)
4. Enriquecimento de logs com contexto distribuído
Em arquiteturas de microsserviços, um mesmo fluxo de usuário atravessa múltiplos serviços. Para correlacionar logs entre eles, propague um correlation_id via headers HTTP ou mensagens em filas:
{
"timestamp": "2025-01-15T10:30:45.123Z",
"level": "INFO",
"message": "Pagamento processado",
"service": "payment-service",
"correlation_id": "corr-xyz-789",
"trace_id": "trace-0a1b2c3d",
"span_id": "span-4e5f6a7b"
}
A integração com OpenTelemetry permite que logs, traces e métricas compartilhem o mesmo trace_id, criando uma visão unificada do sistema. Middlewares em gateways ou frameworks web podem injetar esses IDs automaticamente em cada requisição.
5. Ferramentas de coleta e análise de logs JSON
Ferramentas de agregação como Fluentd, Logstash e Vector reconhecem automaticamente logs JSON, eliminando a necessidade de parsers customizados. Basta configurar o input para aceitar JSON e o output para o destino desejado.
Para armazenamento e consulta, duas stacks dominam o mercado:
- ELK (Elasticsearch + Logstash + Kibana): Elasticsearch indexa cada campo JSON automaticamente. Kibana permite criar dashboards e alertas com queries como
level:ERROR AND service:auth-service. - Loki + Grafana: Loki é otimizado para logs, indexando apenas metadados. Grafana consulta logs com LogQL, similar ao PromQL para métricas.
Dica importante: defina mapeamentos explícitos no Elasticsearch para campos como duration_ms (tipo long) e timestamp (tipo date), evitando inferências incorretas que podem degradar a performance de consultas.
6. Boas práticas e armadilhas comuns
Controle de volume: logs DEBUG em produção podem gerar terabytes desnecessários. Use níveis adequados — ERROR para falhas, WARN para anomalias, INFO para eventos importantes. Configure amostragem para logs de alta frequência.
Sanitização de dados sensíveis: nunca logue senhas, tokens JWT, números de cartão ou PII (dados pessoais identificáveis). Crie filtros no logger para mascarar ou omitir campos como password, credit_card e ssn.
Tratamento de exceções: ao capturar exceções, registre o stack trace como campo JSON estruturado, não como string solta:
{
"level": "ERROR",
"message": "Falha ao conectar ao banco",
"error": {
"type": "ConnectionRefused",
"message": "ECONNREFUSED 127.0.0.1:5432",
"stack": "Error: connect ECONNREFUSED\n at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1159:16)"
},
"service": "user-service"
}
7. Monitoramento e alertas baseados em logs JSON
Logs JSON permitem criar métricas acionáveis sem instrumentar código adicional. No Kibana, uma query como level:ERROR AND service:payment-service pode gerar alertas quando a contagem ultrapassa um limiar em 5 minutos.
Exemplo de query para detectar picos de erro 5xx:
kubernetes.namespace:"production" AND http.status_code:[500 TO 599]
No Grafana com Loki, use LogQL para agregar:
sum(count_over_time({service="api-gateway"} |= `"level":"ERROR"` [5m])) > 10
Esses alertas devem ser acionáveis: direcionados ao canal certo (Slack, PagerDuty) e com contexto suficiente (service, região, trace_id) para acelerar a investigação.
8. Evolução: logs JSON como base para observabilidade moderna
Logs estruturados são a espinha dorsal da observabilidade moderna. Com OpenTelemetry, logs, traces e métricas convergem em um único formato, permitindo debugging profundo: um log de erro pode ser correlacionado ao trace completo da requisição e às métricas de latência do serviço.
Tendências emergentes incluem:
- Schema registry para logs: definir schemas JSON válidos para cada tipo de log, garantindo consistência entre equipes.
- Logging assíncrono: buffers em memória que escrevem logs em lote, reduzindo impacto na performance da aplicação.
- High-performance logging: bibliotecas como
zap(Go) epino(Node.js) priorizam baixa alocação de memória e alta taxa de throughput.
Empresas que adotam logs JSON desde o início do projeto reduzem drasticamente o tempo médio de resolução de incidentes e ganham visibilidade real sobre o comportamento de seus sistemas em produção.
Referências
- python-json-logger documentation — Biblioteca Python para formatação de logs em JSON com o módulo logging padrão.
- Pino: Super fast JSON logger for Node.js — Documentação oficial do logger JSON de alto desempenho para Node.js.
- Zap: Blazing fast, structured, leveled logging in Go — Repositório oficial do logger estruturado da Uber para Go.
- Elastic Common Schema (ECS) — Documentação do schema padronizado da Elastic para logs JSON, facilitando consultas no Kibana.
- OpenTelemetry Logging Specification — Especificação oficial da OpenTelemetry para logs estruturados e sua correlação com traces e métricas.
- Loki documentation: LogQL — Guia de consultas LogQL do Grafana Loki para análise de logs JSON em tempo real.
- Fluentd: JSON parsing — Documentação do parser JSON do Fluentd para coleta e roteamento de logs estruturados.
- 12 Factor App: Logs — Princípios de logs como fluxo de eventos, base para logging estruturado em aplicações modernas.