O logging é uma prática essencial no desenvolvimento de software, permitindo rastrear o comportamento de uma aplicação, diagnosticar problemas e monitorar o desempenho. O módulo logging padrão do Python é poderoso e flexível.
Este artigo detalha a estrutura completa de um sistema de logging customizado em Python, demonstrando como encapsulá-lo em um módulo dedicado e utilizá-lo em sua aplicação principal.
1. Módulo de Configuração de Logging (log_config.py)
A seguir, apresentamos o script completo que configura o sistema de logging, incluindo a rotação de arquivos, o registro em console e as funções auxiliares para rastreabilidade.
# log_config.py
import logging
from logging.handlers import TimedRotatingFileHandler
import os
import inspect
import functools
# --- 1. Configuração de Caminhos e Formato ---
LOG_DIR = "logs"
# Cria o diretório 'logs' se ele não existir
os.makedirs(LOG_DIR, exist_ok=True)
LOG_FILE = os.path.join(LOG_DIR, "app_execucao.log")
LOG_FORMAT = "%(asctime)s | %(levelname)s | %(name)s | %(message)s"
# --- 2. Configuração do Handler de Arquivo com Rotação Diária ---
# Rotação diária à meia-noite, mantém 2 backups.
handler = TimedRotatingFileHandler(
LOG_FILE, when="midnight", interval=1, backupCount=2, encoding="utf-8"
)
handler.setFormatter(logging.Formatter(LOG_FORMAT))
handler.setLevel(logging.DEBUG)
# --- 3. Configuração Global do Logger Raiz ---
logger = logging.getLogger()
logger.setLevel(logging.DEBUG) # O logger raiz processa TUDO (Debug+)
logger.addHandler(handler)
# --- 4. Configuração do Handler de Console (Opcional) ---
console = logging.StreamHandler()
console.setFormatter(logging.Formatter(LOG_FORMAT))
console.setLevel(logging.INFO) # O console mostra apenas INFO e superiores
logger.addHandler(console)
# --- 5. Funções Auxiliares com Rastreamento de Caller ---
def _get_caller_name():
"""Retorna o nome da função que chamou a função de log."""
# O frame [2] geralmente aponta para a função que chamou log_info, log_debug, etc.
return inspect.currentframe().f_back.f_back.f_code.co_name
def log_debug(msg):
caller = _get_caller_name()
logging.debug(f"[{caller}] {msg}")
def log_info(msg):
caller = _get_caller_name()
logging.info(f"[{caller}] {msg}")
def log_error(msg):
caller = _get_caller_name()
logging.error(f"[{caller}] {msg}")
# --- 6. Decorador de Fluxo de Execução ---
def log_inicio_fim(func):
"""Decorador que registra o início de uma função."""
@functools.wraps(func)
def wrapper(*args, **kwargs):
logging.info(f"Início do método: {func.__name__}")
try:
result = func(*args, **kwargs)
# logging.info(f"Fim do método: {func.__name__}")
return result
except Exception as e:
logging.error(f"Exceção no método {func.__name__}: {e}", exc_info=True)
raise
return wrapper
# FIM do log_config.py
2. Análise Detalhada da Configuração
O sistema de logging é construído usando o módulo logging e o TimedRotatingFileHandler.
A. Rotação de Arquivo (A Chave da Robustez)
O TimedRotatingFileHandler é o componente central para gerenciamento de arquivos.
| Parâmetro | Valor | Função |
|---|---|---|
when |
"midnight" |
Define que a rotação ocorrerá diariamente à meia-noite. |
interval |
1 |
Rotação a cada 1 unidade de tempo (neste caso, 1 dia). |
backupCount |
2 |
Armazena no máximo os dois arquivos de log rotacionados mais recentes. |
setLevel |
logging.DEBUG |
Este handler registra todas as mensagens (Debug e superiores). |
B. Diferenciação de Níveis
Ao configurar o logger raiz para DEBUG, garantimos que ele receba e distribua todas as mensagens. A diferenciação é feita nos handlers:
- O Arquivo recebe logs
DEBUGe superiores. - O Console recebe logs
INFOe superiores.
C. Rastreabilidade de Função
As funções auxiliares (log_debug, log_info, etc.) usam o módulo inspect para descobrir o nome da função que as chamou, inserindo-o na mensagem (ex: [processar_dados]). Isso é crucial para rastrear o fluxo da aplicação.
3. Exemplo Prático de Uso (main_app.py)
Para utilizar a configuração de log em seu projeto, basta importar as funções e o decorador do módulo log_config.
# main_app.py
# Importa o logging configurado, as funções e o decorador
# A simples importação executa as configurações do logger raiz
from log_config import log_inicio_fim, log_info, log_error, log_debug
@log_inicio_fim
def processar_dados(dados):
"""Função que simula um processamento."""
log_info(f"Recebendo {len(dados)} itens para processamento.")
if not dados:
log_error("A lista de dados está vazia!")
return 0
try:
resultado = sum(dados)
log_debug(f"Soma calculada: {resultado}")
return resultado
except TypeError:
# Lançamos a exceção, mas o decorador log_inicio_fim a captura e loga.
raise
def iniciar_aplicacao():
log_info("Iniciando a sequência de execução da aplicação.")
lista1 = [10, 20, 30, 40]
total1 = processar_dados(lista1)
log_info(f"Processamento 1 concluído. Total: {total1}")
lista2 = []
total2 = processar_dados(lista2)
log_info(f"Processamento 2 concluído. Total: {total2}")
log_info("Aplicação finalizada.")
# Execução
if __name__ == "__main__":
try:
iniciar_aplicacao()
except Exception:
# Erros não tratados são logados (já tratados pelo decorador, mas boa prática)
pass
Saídas Geradas (Resumo)
- Console (Nível INFO+): Mostrará o início de métodos e mensagens de
INFOeERROR. - Arquivo (
logs/app_execucao.log, Nível DEBUG+): Conterá todas as mensagens, incluindo os detalhes de nívelDEBUG(ex:[processar_dados] Soma calculada: 100) e o traceback completo de qualquer exceção capturada pelo decorador.
A arquitetura de logging modularizada garante que a lógica de registro de eventos esteja separada da lógica de negócio da aplicação, resultando em um código mais limpo e de fácil manutenção.