Logging: O que É e Como Funciona | Python Brasil
Domine o modulo logging em Python: niveis, handlers, formatters, configuracao avancada e boas praticas para registrar eventos na sua aplicacao.
O que e Logging?
Logging e o mecanismo de registrar eventos que ocorrem durante a execucao de um programa. O modulo logging da biblioteca padrao do Python oferece um sistema flexivel e configuravel para emitir mensagens de log com diferentes niveis de severidade. Diferente de print(), o logging permite controlar o que e registrado, para onde vai (arquivo, console, servicos externos) e em que formato, sem modificar o codigo da aplicacao.
Em aplicacoes profissionais, logging e essencial para depuracao, monitoramento, auditoria e diagnostico de problemas em producao.
Niveis de Log
O Python define cinco niveis padrao de severidade, em ordem crescente:
import logging
# DEBUG — informacoes detalhadas para diagnostico
logging.debug('Variavel x = 42')
# INFO — confirmacao de que as coisas estao funcionando
logging.info('Servidor iniciado na porta 8000')
# WARNING — algo inesperado ou potencial problema
logging.warning('Disco com 90%% de uso')
# ERROR — erro que impede uma funcionalidade
logging.error('Falha ao conectar ao banco de dados')
# CRITICAL — erro grave que pode encerrar o programa
logging.critical('Memoria insuficiente, encerrando')
Configuracao Basica
import logging
# Configuracao rapida
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
)
# Usando
logging.info('Aplicacao iniciada')
logging.warning('Configuracao nao encontrada, usando padrao')
Loggers, Handlers e Formatters
O sistema de logging do Python e composto por tres componentes principais que trabalham juntos.
import logging
# Criar logger com nome (geralmente __name__)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# Formatter — define o formato da mensagem
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
)
# Handler para console
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formatter)
# Handler para arquivo
file_handler = logging.FileHandler('app.log', encoding='utf-8')
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
# Adicionar handlers ao logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# Usar o logger
logger.debug('Informacao detalhada (so vai para o arquivo)')
logger.info('Informacao geral (console e arquivo)')
logger.error('Erro encontrado (console e arquivo)')
Logging com Rotacao de Arquivos
import logging
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
logger = logging.getLogger('minha_app')
logger.setLevel(logging.INFO)
# Rotacao por tamanho
handler_tamanho = RotatingFileHandler(
'app.log',
maxBytes=5 * 1024 * 1024, # 5 MB
backupCount=5, # manter 5 arquivos antigos
encoding='utf-8',
)
# Rotacao por tempo
handler_tempo = TimedRotatingFileHandler(
'app.log',
when='midnight', # rotacionar a meia-noite
interval=1, # a cada 1 dia
backupCount=30, # manter 30 dias
encoding='utf-8',
)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler_tamanho.setFormatter(formatter)
logger.addHandler(handler_tamanho)
Configuracao com Dicionario
import logging
import logging.config
config = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'padrao': {
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
},
'simples': {
'format': '%(levelname)s - %(message)s',
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'INFO',
'formatter': 'simples',
},
'arquivo': {
'class': 'logging.handlers.RotatingFileHandler',
'level': 'DEBUG',
'formatter': 'padrao',
'filename': 'app.log',
'maxBytes': 10485760,
'backupCount': 5,
},
},
'loggers': {
'': { # root logger
'level': 'DEBUG',
'handlers': ['console', 'arquivo'],
},
},
}
logging.config.dictConfig(config)
logger = logging.getLogger(__name__)
Logging Estruturado
import logging
import json
class JsonFormatter(logging.Formatter):
"""Formatter que gera logs em JSON."""
def format(self, record):
log_data = {
'timestamp': self.formatTime(record),
'level': record.levelname,
'logger': record.name,
'message': record.getMessage(),
}
if record.exc_info:
log_data['exception'] = self.formatException(record.exc_info)
if hasattr(record, 'extra_data'):
log_data['data'] = record.extra_data
return json.dumps(log_data, ensure_ascii=False)
# Configurar
logger = logging.getLogger('api')
handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter())
logger.addHandler(handler)
logger.setLevel(logging.INFO)
# Usar
logger.info('Requisicao recebida', extra={'extra_data': {'metodo': 'GET', 'path': '/api/users'}})
Logging em Funcoes e Classes
import logging
logger = logging.getLogger(__name__)
def processar_pedido(pedido_id: int) -> dict:
"""Processa um pedido com logging adequado."""
logger.info('Iniciando processamento do pedido %d', pedido_id)
try:
# logica de processamento
logger.debug('Validando pedido %d', pedido_id)
resultado = {'pedido_id': pedido_id, 'status': 'processado'}
logger.info('Pedido %d processado com sucesso', pedido_id)
return resultado
except ValueError as e:
logger.warning('Dados invalidos no pedido %d: %s', pedido_id, e)
raise
except Exception as e:
logger.exception('Erro inesperado ao processar pedido %d', pedido_id)
raise
class ServicoEmail:
def __init__(self):
self.logger = logging.getLogger(f'{__name__}.{self.__class__.__name__}')
def enviar(self, destinatario: str, assunto: str):
self.logger.info('Enviando email para %s: %s', destinatario, assunto)
Erros Comuns
O erro mais frequente e usar print() em vez de logging para depuracao, tornando dificil controlar e filtrar as mensagens. Outro erro e chamar logging.basicConfig() multiplas vezes esperando que reconfigure — ele so funciona na primeira chamada. Tambem e comum incluir dados sensiveis como senhas e tokens em mensagens de log. Usar logging.exception() fora de um bloco except e outro erro, pois ele espera informacoes de excecao ativa. Formatar a string antes de passar para o logger com f-strings (logger.info(f'valor: {x}')) e menos eficiente que usar formatacao preguicosa (logger.info('valor: %s', x)).
Boas Praticas
Use logging.getLogger(__name__) para criar loggers com nomes hierarquicos. Prefira formatacao preguicosa (%s) em vez de f-strings em chamadas de log. Use logger.exception() dentro de blocos except para incluir o traceback. Configure logging no ponto de entrada da aplicacao, nao dentro de bibliotecas. Nunca registre dados sensiveis. Use niveis de log apropriados — DEBUG para desenvolvimento, INFO para operacao normal, WARNING para situacoes inesperadas.
Quando Usar
Logging deve ser usado em toda aplicacao que va para producao. Substitua print() por logging desde o inicio do projeto. Em APIs web, registre cada requisicao com tempo de resposta. Em scripts de processamento, registre progresso e erros. Em bibliotecas, use logging mas deixe a configuracao para a aplicacao consumidora.