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 DEBUG e superiores.
  • O Console recebe logs INFO e 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 INFO e ERROR.
  • Arquivo (logs/app_execucao.log, Nível DEBUG+): Conterá todas as mensagens, incluindo os detalhes de nível DEBUG (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.