Registro Estruturado
O registro estruturado anexa contexto de chave-valor a cada linha de log - tipicamente JSON - para que os operadores possam filtrar por user_id, order_id ou trace_id sem arqueologia de regex.
Receita
import structlog
structlog.configure(
processors=[
structlog.processors.add_log_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.JSONRenderer(),
],
)
log = structlog.get_logger()
log.info("order_created", order_id="ord_1", amount_cents=4200)Quando usar isso:
- Todo serviço Python de produção atrás de contêineres ou systemd
- Plataformas de log centralizadas (Loki, Datadog, CloudWatch Logs Insights)
- Correlação com traces e campos de métricas
- Auditorias de segurança que necessitam de campos de ator/ação pesquisáveis
Exemplo de Trabalho
structlog com contextvars para request_id e vinculação de middleware FastAPI.
import uuid
from contextvars import ContextVar
import structlog
from fastapi import FastAPI, Request
request_id_var: ContextVar[str] = ContextVar("request_id", default="-")
def configure_logging() -> None:
structlog.configure(
processors=[
structlog.contextvars.merge_contextvars,
structlog.processors.add_log_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.JSONRenderer(),
],
)
configure_logging()
log = structlog.get_logger()
app = FastAPI()
@app.middleware("http")
async def bind_request_context(request: Request, call_next):
rid = request.headers.get("x-request-id", str(uuid.uuid4()))
structlog.contextvars.clear_contextvars()
structlog.contextvars.bind_contextvars(request_id=rid, path=request.url.path)
response = await call_next(request)
response.headers["X-Request-ID"] = rid
return response
@app.get("/orders/{order_id}")
async def get_order(order_id: str):
log.info("fetch_order", order_id=order_id)
return {"order_id": order_id}O que isso demonstra:
bind_contextvarsadiciona campos a todos os logs no escopo da requisição- O middleware propaga ou gera
X-Request-ID - O renderizador JSON produz um objeto analisável por linha
Mergulho Profundo
Pipeline de Processadores
| Processador | Papel |
|---|---|
| merge_contextvars | Injetar contexto da requisição |
| add_log_level | Campo level |
| TimeStamper | Timestamp ISO |
| JSONRenderer | Serializar |
Integração stdlib
import logging
structlog.stdlib.recreate_defaults() # ponte para a configuração de loggingNotas Python
# Nunca registre segredos
log.info("token_refreshed", user_id=u.id) # não: token=tokenArmadilhas
- f-strings em mensagens de log - campos não indexados. Correção: argumentos de palavra-chave
log.info("event", user_id=id). - Registrar PII sem política - violações do GDPR. Correção: permitir campos por lista branca, hashear IDs de usuário em não produção.
- Grandes dumps de payload - custo e ruído. Correção: registrar tamanho/hash, não o corpo completo.
- Mistura de texto puro e JSON - o analisador quebra. Correção: apenas JSON em produção; console formatado em desenvolvimento.
- Falta de trace_id - não é possível pular para APM. Correção: vincular contexto de trace OpenTelemetry em um processador.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| stdlib logging Formatter JSON | Dependências mínimas | Quer a ergonomia do pipeline de processadores |
| loguru | Scripts e protótipos | Equipe padronizada em structlog |
| print(JSON) | Debug rápido | Serviços de produção |
FAQs
structlog vs logging?
structlog constrói eventos estruturados; integra-se com manipuladores de log stdlib para transporte.
Como faço para imprimir formatado localmente?
Use ConsoleRenderer() na cadeia de processadores de desenvolvimento; troque para JSONRenderer em produção via env.
Integração com Django?
Middleware vincula contextvars; configure structlog no dicionário LOGGING substituindo formatadores.
Como registro exceções?
log.exception("payment_failed", order_id=oid) ou exc_info=True na ponte stdlib.
Amostragem de logs?
Amostre o volume de debug/trace no nível do processador; nunca amostre ERROR sem política explícita.
Como os workers obtêm contexto?
Vincule job_id no início da tarefa; propague cabeçalhos de trace dos atributos da mensagem de enfileiramento.
CloudWatch Logs Insights?
Campos JSON se tornam consultáveis - fields @timestamp, order_id | filter level='error'.
Custo de desempenho?
Serialização JSON é barata em comparação com I/O; evite appenders de rede síncronos que bloqueiam o caminho da requisição.
Como isso se relaciona com a análise de logs?
Os aplicativos devem emitir JSON upstream; analisar logs de texto é um fallback para sistemas legados.
Campos obrigatórios?
timestamp, level, event/message, nome do serviço, ambiente, request_id/trace_id quando disponíveis.
Relacionado
- Noções Básicas de Observabilidade - três pilares
- Rastreamento Distribuído - trace_id em logs
- Rastreamento de Erros - Sentry complementa logs
- Saúde e Prontidão - logs de acesso mínimos
- Melhores Práticas de Observabilidade - política de logging
Versões de Stack: Esta página foi escrita para Python 3.14.0 (estável 3.14, manutenção 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, e uv 0.6+.