(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisboa

Como fazer logging num pipeline ETL: passo a passo

João Barros 05 de July de 2026 5 min de leitura

Um pipeline ETL que corre em silêncio é um problema à espera de acontecer: quando algo falha a meio da noite, não sabes onde nem porquê. Adicionar logging a um pipeline ETL dá-te um registo claro de cada fase — extração, transformação e carga — para detetares erros cedo, medires a duração e saberes quantas linhas foram processadas. Ao contrário de um simples print(), o logging dá-te níveis de gravidade, data e hora automáticas e a possibilidade de escrever em vários destinos ao mesmo tempo. Vais construir, passo a passo, um sistema simples de logging e monitorização em Python, usando apenas a biblioteca-padrão.

Pré-requisitos

  • Python 3.9 ou superior instalado.
  • Um pipeline ETL, mesmo que pequeno (ler dados, transformar, gravar).
  • Conhecimentos básicos de funções em Python.
  • Um editor de código, como o VS Code.

Passo 1: Configurar o logging básico

O módulo logging faz parte da biblioteca-padrão do Python, por isso não precisas de instalar nada. Define primeiro o formato das mensagens e o nível mínimo que queres registar. O nível INFO é ideal para acompanhar o progresso no dia a dia; guarda o DEBUG para quando estás mesmo a investigar um problema.

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s | %(levelname)s | %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)

logger = logging.getLogger("etl")
logger.info("Pipeline ETL iniciado")

Passo 2: Registar cada fase do pipeline

Regista o início de cada fase e, sobretudo, o número de linhas processadas. Estas contagens são a forma mais simples de monitorização: se um dia extraíres 0 linhas onde costumavas ter milhares, o log denuncia logo o problema. Usa o nível INFO para o fluxo normal e o nível WARNING quando algo é estranho mas não impede o pipeline de continuar.

def extract():
    logger.info("Extração: a ler dados da origem")
    rows = read_source()          # a tua função de leitura
    logger.info("Extração: %d linhas lidas", len(rows))
    return rows

def transform(rows):
    logger.info("Transformação: a aplicar regras")
    clean = [r for r in rows if r["valor"] is not None]
    logger.info("Transformação: %d válidas de %d", len(clean), len(rows))
    return clean

Passo 3: Guardar os logs em ficheiro com rotação

Ver as mensagens no ecrã ajuda durante o desenvolvimento, mas em produção precisas de as guardar. O RotatingFileHandler escreve num ficheiro e cria automaticamente um novo quando o atual atinge o tamanho-limite, evitando ficheiros gigantes que enchem o disco.

from logging.handlers import RotatingFileHandler

file_handler = RotatingFileHandler(
    "etl.log", maxBytes=1_000_000, backupCount=5
)
file_handler.setFormatter(
    logging.Formatter("%(asctime)s | %(levelname)s | %(message)s")
)
logger.addHandler(file_handler)

Passo 4: Capturar e registar erros

Envolve cada fase num bloco try/except. O método logger.exception() regista a mensagem e o traceback completo, o que torna a análise do erro muito mais rápida. Relança a exceção a seguir para o pipeline parar de forma controlada, em vez de continuar com dados incompletos. Assim, os erros nunca passam despercebidos e ficam guardados com todo o contexto.

def load(rows):
    try:
        logger.info("Carga: a gravar %d linhas", len(rows))
        write_target(rows)        # a tua função de escrita
        logger.info("Carga: concluída com sucesso")
    except Exception:
        logger.exception("Carga: falhou ao gravar no destino")
        raise

Passo 5: Medir a duração de cada execução

A duração é uma métrica de monitorização muito útil: um pipeline que costuma demorar 2 minutos e passa a demorar 30 é um sinal de alerta claro. Usa time.perf_counter() para medir e registar o tempo total de cada execução.

import time

def run_pipeline():
    start = time.perf_counter()
    logger.info("=== Execução iniciada ===")
    rows = transform(extract())
    load(rows)
    elapsed = time.perf_counter() - start
    logger.info("=== Execução concluída em %.1f s ===", elapsed)

if __name__ == "__main__":
    run_pipeline()

Verificar o resultado

Corre o pipeline e abre o ficheiro etl.log. Deves ver uma linha por cada fase, com data, hora, nível e mensagem — incluindo as contagens de linhas e a duração final. Para testar o tratamento de erros, força uma falha de propósito (por exemplo, aponta a leitura para um ficheiro que não existe) e confirma que aparece uma linha ERROR com o traceback. Depois, filtra só os problemas com um comando rápido:

grep ERROR etl.log

Conclusão

Com poucas linhas de código passaste de um pipeline "caixa preta" para um processo observável: sabes o que corre, quanto demora e onde falha. O passo seguinte natural é enviar estes logs para uma ferramenta central, como o Azure Monitor ou o Application Insights, e criar alertas automáticos sempre que surgir um ERROR. Qual das métricas achas mais importante vigiar no teu pipeline: o número de linhas processadas ou o tempo de execução?