Depuração em Produção
Bugs em produção diferem de falhas locais: o formato do tráfego, o volume de dados e a configuração do ambiente escondem problemas que seu laptop nunca vê. Logs estruturados, traces distribuídos e profiling ao vivo seguro fecham essa lacuna sem reinícios arriscados.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
import logging
import structlog
structlog.configure(
processors=[
structlog.contextvars.merge_contextvars,
structlog.processors.add_log_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.JSONRenderer(),
],
wrapper_class=structlog.make_filtering_bound_logger(logging.INFO),
)
log = structlog.get_logger()
def handle_request(request_id: str, trace_id: str, tenant: str) -> None:
structlog.contextvars.bind_contextvars(
request_id=request_id, trace_id=trace_id, tenant=tenant
)
log.info("order_created", order_id="ord_123")# Amostra de pilha ao vivo (sem reinício, precisa de ptrace ou CAP_SYS_PTRACE)
py-spy top --pid $(pgrep -f "gunicorn.*api:app")
py-spy dump --pid <pid> --lines 50Quando usar isso:
- Taxa de erro aumenta sem correlação óbvia com deploy
- Latência cresce, mas CPU parece ociosa (GIL ou espera de I/O)
- Problema aparece apenas para um tenant ou região
- Reprodução local falha após combinar versão do Python e variáveis de ambiente
Exemplo de Trabalho
"""prod_debug_helpers.py - toolkit mínimo para correlacionar evidências de produção."""
from __future__ import annotations
import json
import os
import sys
import time
from contextlib import contextmanager
from dataclasses import dataclass, asdict
import structlog
structlog.configure(
processors=[
structlog.contextvars.merge_contextvars,
structlog.processors.add_log_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.JSONRenderer(),
],
)
log = structlog.get_logger()
@dataclass(frozen=True)
class DeployContext:
git_sha: str
python_version: str
service: str
def deploy_context() -> DeployContext:
return DeployContext(
git_sha=os.getenv("GIT_SHA", "unknown"),
python_version=sys.version.split()[0],
service=os.getenv("SERVICE_NAME", "api"),
)
@contextmanager
def request_scope(request_id: str, trace_id: str, **extra: str):
structlog.contextvars.bind_contextvars(
request_id=request_id,
trace_id=trace_id,
**extra,
**asdict(deploy_context()),
)
start = time.perf_counter()
try:
yield
finally:
log.info("request_finished", duration_ms=(time.perf_counter() - start) * 1000)
def simulate_slow_path() -> None:
with request_scope("req-9f2", "trace-abc", tenant="acme"):
log.info("cache_miss", key="user:42")
time.sleep(0.05) # substitui a ida e volta ao banco de dados
log.warning("downstream_retry", attempt=2, service="billing")
if __name__ == "__main__":
simulate_slow_path()
print("Filtrar logs de produção: trace_id=trace-abc AND level>=warning")O que isso demonstra:
- Logs JSON carregam
request_id,trace_ide metadados de deploy em cada linha - Gerenciadores de contexto vinculam o escopo uma vez por requisição em vez de passar kwargs por toda parte
- Duração e retentativas downstream aparecem no mesmo stream de log que eventos de negócio
- SHA do deploy nos logs conecta incidentes a artefatos de release rapidamente
Mergulho Profundo
Como Funciona
- Logging Estruturado - Campos chave-valor indexam em Loki, CloudWatch ou Datadog; grep de texto puro em escala falha.
- Propagação de Trace - OpenTelemetry injeta
trace_idem logs quando o SDK é configurado com uma ponte de logging. - py-spy - Coleta pilhas de chamadas CPython externamente; seguro em workers Gunicorn/Uvicorn sob carga.
- Reprodução somente leitura - Copie payloads de produção anonimizados e estado de feature-flag para staging antes de alterações de código.
- Linha do tempo de evidências - Alinhe logs, métricas e anotações de deploy em uma única janela UTC.
Checklist de Sinais de Produção
| Sinal | Ferramenta | O que revela |
|---|---|---|
| Picos de erro | Sentry / logs | Stack traces, release, impacto no usuário |
| Latência | APM p95/p99 | Spans lentos, consultas N+1 |
| CPU vs espera | py-spy top | Contenção do GIL, loops intensos |
| Memória | Gráficos RSS, tracemalloc | Vazamentos após deploy |
| Deriva de configuração | Diferença de env staging vs prod | Segredos ausentes, tamanho incorreto do pool |
Notas de Python
# Habilita faulthandler na inicialização para hangs nativos
import faulthandler
faulthandler.enable()
# Debug temporário em apenas um pod (nunca comite)
import logging
logging.getLogger("sqlalchemy.engine").setLevel(logging.INFO)Armadilhas
- Reiniciar pods antes de capturar logs - Você perde estado em memória e evidências de pilha. Correção: Capture logs,
py-spy dumpe métricas de heap primeiro. - IDs de correlação ausentes - Milhares de erros idênticos são inoperantes para busca. Correção: Exija
request_id/trace_idno middleware antes que os handlers executem. - Depuração com nível DEBUG globalmente - O volume de logs explode e PII vaza. Correção: Aumente o nível em um pod ou use amostragem dinâmica.
- Assumir que local == prod - Diferentes
PYTHONHASHSEED, locale ou pins de dependência mudam o comportamento. Correção: Combine o digest da imagem e o env em um ambiente de scratch. - Bloquear o event loop em apps async - Chamadas síncronas de ORM travam todas as requisições; a CPU parece baixa. Correção: Verifique as linhas do tempo dos spans por segmentos síncronos longos; mova I/O para fora do loop.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| Debugador remoto (debugpy) | Repro de staging confirmada | Tráfego de produção (inseguro, lento) |
| Replay de logs de acesso | Caminhos de leitura idempotentes | Escritas com efeitos colaterais |
| Bisseção de feature flag | Suspeita de novo caminho de código | Falha apenas de infraestrutura |
| Dump de heap (memray) | Crescimento de memória ao longo de horas | Loops intensos limitados por CPU |
FAQs
Como depuro sem acesso SSH?
Use plataformas de log, APM e kubectl exec/ecs exec apenas em um único pod canário. Prefira scripts py-spy e env-print commitados no repositório em vez de edições ad hoc.
Devo habilitar Python -X dev em produção?
Não. O modo de desenvolvimento adiciona verificações e avisos caros. Use staging com -X dev e mantenha a produção com logging ajustado e probes de saúde.
Qual é a primeira consulta em um incidente?
Filtre erros por serviço e git_sha nas últimas duas horas, depois pivote em trace_id da requisição bem-sucedida mais lenta para contraste.
Como rastreio uma tarefa Celery?
Passe trace_id nos kwargs da tarefa e vincule os contextvars do structlog na classe base da tarefa. Conecte logs do worker aos logs da API através do mesmo ID.
Quando py-spy não é suficiente?
Quando o processo está bloqueado em código nativo sem frames Python, ou durante a inicialização antes que os workers se vinculem. Combine com eBPF ou profiling nativo APM.
Posso usar pprint em produção?
Apenas atrás de uma feature flag e limite de taxa. Prefira campos estruturados para que os dashboards possam agregar.
Como comparo configurações de staging e produção?
Exporte chaves de env redigidas (apenas nomes) e compare tamanhos de pool, valores de timeout e feature flags. Nunca cole segredos em tickets.
E o Python 3.14 com free-threading?
Paradas relacionadas ao GIL diminuem, mas I/O e contenção de locks permanecem. py-spy ainda ajuda; observe os pontos intensos de threading.Lock nas métricas.
Por quanto tempo devo manter o logging de depuração ativado?
Minutos para um pod. Revertar imediatamente após a captura; documente as descobertas no canal de incidentes.
Quem deve ser o responsável pela depuração em produção?
O engenheiro de plantão conduz a coleta de evidências; o proprietário do serviço interpreta a lógica de domínio. Não depure SEV1 sozinho sem um comandante de incidentes.
Relacionados
- Playbooks de Resposta a Incidentes - papéis e severidade dos primeiros 15 minutos
- Modos Comuns de Falha em Produção - padrões de outage específicos do Python
- Runbooks de Rollback e Recuperação - estabilize sob pressão
- Logging Estruturado - configuração de logs JSON
- Distributed Tracing - propagação de trace
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+.