Logging em Python — 2025 | Python Brasil
Domine o módulo logging em Python. Níveis, formatação, handlers e boas práticas profissionais. Aprenda agora!
O módulo logging é uma das ferramentas mais subutilizadas por desenvolvedores Python iniciantes e intermediários. Enquanto print() pode parecer suficiente durante o desenvolvimento, em produção ele é completamente inadequado. O logging profissional permite categorizar mensagens por severidade, direcionar logs para diferentes destinos, formatar saídas de maneira padronizada e desativar ou ativar mensagens sem alterar o código.
Neste artigo, vamos explorar o módulo logging do Python em profundidade, desde o uso básico até configurações avançadas para aplicações profissionais.
Por que não usar print()?
Antes de mergulhar no logging, vale entender por que print() é insuficiente em projetos reais:
# O problema com print()
print("Iniciando processamento...") # Sem contexto temporal
print("ERRO: arquivo não encontrado") # Sem severidade formal
print(f"Processando item {item}") # Não pode ser desligado facilmente
print("DEBUG: valor da variável x =", x) # Vai para produção por acidente
O print() mistura todas as mensagens em um único fluxo sem distinção de importância, sem timestamp, sem informação de origem e sem possibilidade de controle granular. O módulo logging resolve todos esses problemas.
Configuração básica
O jeito mais rápido de começar com logging:
import logging
# Configuração básica
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
logger = logging.getLogger(__name__)
# Usando diferentes níveis
logger.debug("Variável x = 42") # Não aparece (nível INFO configurado)
logger.info("Processamento iniciado") # Aparece
logger.warning("Disco com 85% de uso") # Aparece
logger.error("Falha na conexão com DB") # Aparece
logger.critical("Sistema fora do ar") # Aparece
Os cinco níveis de severidade, em ordem crescente, são: DEBUG, INFO, WARNING, ERROR e CRITICAL. Ao configurar o nível como INFO, todas as mensagens de nível INFO ou superior são exibidas, enquanto DEBUG é ignorado.
Níveis de log e quando usá-los
Cada nível tem um propósito específico:
import logging
logger = logging.getLogger("minha_app")
# DEBUG: informações detalhadas para diagnóstico
# Use para rastrear o fluxo do programa durante desenvolvimento
logger.debug("Consultando banco: SELECT * FROM users WHERE id = %s", user_id)
# INFO: confirmação de que as coisas estão funcionando
# Use para eventos normais e esperados
logger.info("Servidor iniciado na porta 8000")
logger.info("Usuário %s realizou login", username)
# WARNING: algo inesperado aconteceu, mas o programa continua
# Use para situações que merecem atenção
logger.warning("Tentativa de login com senha incorreta para %s", username)
logger.warning("API externa respondeu em %dms (limite: 500ms)", tempo)
# ERROR: o programa não conseguiu realizar uma operação
# Use quando algo falhou mas o sistema continua rodando
logger.error("Falha ao enviar e-mail para %s: %s", email, erro)
# CRITICAL: erro grave que pode impedir o programa de continuar
# Use para falhas que comprometem o sistema
logger.critical("Banco de dados principal inacessível")
Handlers: direcionando logs
Handlers definem para onde as mensagens de log são enviadas. Você pode ter múltiplos handlers, cada um com seu próprio nível e formato:
import logging
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
def configurar_logging():
"""Configura logging com múltiplos handlers."""
logger = logging.getLogger("minha_app")
logger.setLevel(logging.DEBUG)
# Formato detalhado para arquivo
formato_arquivo = logging.Formatter(
"%(asctime)s | %(name)s | %(levelname)-8s | %(filename)s:%(lineno)d | %(message)s"
)
# Formato simplificado para console
formato_console = logging.Formatter(
"%(asctime)s - %(levelname)-8s - %(message)s",
datefmt="%H:%M:%S"
)
# Handler de console (INFO e acima)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formato_console)
# Handler de arquivo com rotação por tamanho
arquivo_handler = RotatingFileHandler(
"app.log",
maxBytes=5_000_000, # 5 MB por arquivo
backupCount=5, # Mantém 5 arquivos de backup
encoding="utf-8"
)
arquivo_handler.setLevel(logging.DEBUG)
arquivo_handler.setFormatter(formato_arquivo)
# Handler separado para erros
erro_handler = logging.FileHandler(
"erros.log",
encoding="utf-8"
)
erro_handler.setLevel(logging.ERROR)
erro_handler.setFormatter(formato_arquivo)
# Adicionando handlers ao logger
logger.addHandler(console_handler)
logger.addHandler(arquivo_handler)
logger.addHandler(erro_handler)
return logger
logger = configurar_logging()
logger.info("Sistema iniciado com sucesso")
logger.debug("Modo debug ativo")
logger.error("Exemplo de erro registrado")
Com essa configuração, mensagens DEBUG vão apenas para o arquivo app.log, mensagens INFO e acima aparecem no console e no arquivo, e erros são adicionalmente registrados em erros.log.
Logging com contexto usando extras
Adicionar contexto às mensagens de log facilita a depuração:
import logging
logger = logging.getLogger("api")
def processar_requisicao(request_id, usuario, endpoint):
"""Processa uma requisição com contexto nos logs."""
extra = {"request_id": request_id, "usuario": usuario}
logger.info(
"Requisição recebida: %s %s",
"GET", endpoint,
extra=extra
)
try:
# Simulação de processamento
resultado = {"status": "ok", "dados": [1, 2, 3]}
logger.info(
"Requisição processada com sucesso: %d registros",
len(resultado["dados"]),
extra=extra
)
return resultado
except Exception as e:
logger.exception(
"Erro ao processar requisição para %s",
endpoint,
extra=extra
)
return None
O método logger.exception() registra automaticamente o traceback completo da exceção, sendo muito mais informativo que logger.error() dentro de um bloco except.
Configuração via dicionário
Para projetos maiores, a configuração via dicionário é mais organizada e flexível:
import logging
import logging.config
CONFIG_LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"padrao": {
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S"
},
"detalhado": {
"format": "%(asctime)s | %(name)s | %(levelname)-8s | %(module)s:%(funcName)s:%(lineno)d | %(message)s"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "INFO",
"formatter": "padrao",
"stream": "ext://sys.stdout"
},
"arquivo": {
"class": "logging.handlers.RotatingFileHandler",
"level": "DEBUG",
"formatter": "detalhado",
"filename": "aplicacao.log",
"maxBytes": 10485760,
"backupCount": 3,
"encoding": "utf-8"
}
},
"loggers": {
"minha_app": {
"level": "DEBUG",
"handlers": ["console", "arquivo"],
"propagate": False
},
"minha_app.banco": {
"level": "WARNING",
"handlers": ["console", "arquivo"],
"propagate": False
}
},
"root": {
"level": "WARNING",
"handlers": ["console"]
}
}
logging.config.dictConfig(CONFIG_LOGGING)
# Usando loggers específicos
logger_app = logging.getLogger("minha_app")
logger_banco = logging.getLogger("minha_app.banco")
logger_app.info("Aplicação iniciada")
logger_banco.debug("Query executada") # Não aparece (nível WARNING)
logger_banco.warning("Conexão lenta com o banco")
Logging em módulos separados
Em projetos com múltiplos módulos, cada módulo deve criar seu próprio logger:
# arquivo: servicos/usuario.py
import logging
logger = logging.getLogger(__name__)
class ServicoUsuario:
def criar(self, nome, email):
logger.info("Criando usuário: %s (%s)", nome, email)
try:
# Lógica de criação
logger.info("Usuário %s criado com sucesso", nome)
return True
except Exception:
logger.exception("Falha ao criar usuário %s", nome)
return False
# arquivo: servicos/pedido.py
import logging
logger = logging.getLogger(__name__)
class ServicoPedido:
def processar(self, pedido_id):
logger.info("Processando pedido %s", pedido_id)
# Lógica de processamento
Usar __name__ como nome do logger cria automaticamente uma hierarquia baseada na estrutura de pacotes, facilitando a configuração de níveis diferentes para cada módulo.
Boas práticas de logging
Para um sistema de logging profissional:
- Use
__name__como nome do logger: cria hierarquia automática e facilita o controle. - Nunca use print() em produção: substitua por logging com o nível adequado.
- Use formatação lazy: prefira
logger.info("Valor: %s", x)em vez delogger.info(f"Valor: {x}"). A formatação lazy só processa a string se o log for realmente emitido. - Registre exceções com
exception(): dentro de blocos except, uselogger.exception()para incluir o traceback. - Não logue dados sensíveis: senhas, tokens e dados pessoais nunca devem aparecer em logs.
- Configure rotação de arquivos: use RotatingFileHandler ou TimedRotatingFileHandler para evitar que logs consumam todo o disco.
- Estruture os logs: em sistemas distribuídos, considere usar logging em formato JSON para facilitar a análise com ferramentas como ELK Stack.
Conclusão
O módulo logging é uma ferramenta fundamental para qualquer aplicação Python profissional. Ele oferece flexibilidade para direcionar mensagens para diferentes destinos, controlar a verbosidade por módulo e formatar saídas de maneira padronizada. Investir tempo para configurar o logging adequadamente no início do projeto economiza muitas horas de depuração no futuro.
Como próximos passos, explore bibliotecas como structlog para logging estruturado, aprenda a integrar logs com ferramentas de monitoramento como Grafana e Prometheus, e estude como configurar alertas automáticos baseados em padrões de erro nos seus logs.
Equipe Python Brasil
Contribuidor do Python Brasil — Aprenda Python em Português