Módulo 12 - Exceções, logging e robustez

O módulo logging: adeus, print

11 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026

O que você vai aprender

  • Entender por que print não serve para produção.
  • Criar um logger com getLogger e registrar em níveis DEBUG a ERROR.
  • Configurar handlers e formatters para destino e formato.
  • Registrar exceções com o rastreamento completo.

Por que não usar print em produção

O print é excelente para explorar uma ideia no Playground ou depurar rápido. Para um programa que roda de verdade, ele é fraco. Todo print sai igual, sem indicar se aquilo é um detalhe de depuração ou um erro grave. Não dá para desligar os prints de depuração sem sair apagando linhas. Tudo vai para a mesma saída, sem carimbo de hora, sem o nome de quem registrou, sem para onde mandar. E, para silenciar ou redirecionar, você teria que editar o código. O logging resolve tudo isso de forma configurável.

print

  • Uma saída só, sem níveis de severidade
  • Para desligar, é preciso apagar linhas
  • Sem hora, origem ou formato padrão
  • Bom para explorar, ruim para produção

logging

  • Cinco níveis, de DEBUG a CRITICAL
  • Filtra por nível sem tocar no código
  • Hora, origem e formato configuráveis
  • O padrão profissional para registrar

A ideia central do logging é separar duas decisões que o print mistura. Uma é o que registrar, que fica no seu código, espalhado onde os eventos acontecem. A outra é o que ver e para onde mandar, que fica na configuração, num só lugar. Assim você instrumenta o código uma vez, com mensagens em níveis apropriados, e depois decide, sem editar nada, se quer ver só os erros em produção ou todo o detalhe de depuração enquanto investiga um problema.

Níveis e o logger nomeado

O logging tem cinco níveis de severidade, do menos ao mais grave: DEBUG, para detalhes finos de diagnóstico; INFO, para o curso normal dos eventos; WARNING, para algo inesperado que não impediu o programa; ERROR, para uma falha que atrapalhou uma operação; e CRITICAL, para um problema grave que ameaça o programa inteiro. Você registra cada mensagem no nível certo, e configura o mínimo que quer ver. Definir o nível em WARNING, por exemplo, esconde DEBUG e INFO e mostra o resto.

import logging

# Obtenha um logger nomeado por modulo, nunca o logger raiz.
logger = logging.getLogger(__name__)

def processar(valor):
    logger.debug("iniciando com valor=%s", valor)
    if valor < 0:
        logger.warning("valor negativo recebido: %s", valor)
    try:
        return 100 / valor
    except ZeroDivisionError:
        logger.error("divisao por zero para valor=%s", valor)
        return None

getLogger(__name__) dá um logger por módulo; cada evento sai no nível certo.

Handlers, formatters e exceções

Onde e como o log aparece é papel dos handlers e formatters, e essa é a parte da configuração, feita uma vez, perto do início da aplicação. Um handler define o destino: o console, um arquivo, um serviço remoto. Um formatter define o formato de cada linha: hora, nível, nome do logger e mensagem. Para começar, logging.basicConfig monta um handler de console com um formato razoável em uma linha, o suficiente para muitos programas. Aplicações maiores configuram handlers com mais cuidado, inclusive vários ao mesmo tempo.

import logging

# Configuracao feita uma vez, no ponto de entrada da aplicacao.
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(name)s: %(message)s",
)

logger = logging.getLogger("app.pagamentos")

def cobrar(valor):
    try:
        if valor <= 0:
            raise ValueError("valor deve ser positivo")
        logger.info("cobranca de R$ %s realizada", valor)
    except ValueError:
        # exception() registra em ERROR com o rastreamento completo.
        logger.exception("falha ao cobrar")

cobrar(0)

basicConfig define nível e formato; logger.exception grava o rastreamento inteiro.

O destaque prático é o logger.exception. Chamado dentro de um bloco except, ele registra a mensagem no nível ERROR e anexa o rastreamento completo da exceção, sem você precisar montá-lo à mão. É a forma correta de registrar uma falha: a mensagem diz o que estava acontecendo, e o rastreamento diz exatamente onde e por quê. Comparado a um print do erro, que perde o rastro e o contexto, o logging entrega um registro que uma pessoa consegue investigar horas depois, em produção, sem estar na frente do código.

Teste rápido

Qual a principal vantagem do logging sobre o print em produção?

Perguntas frequentes

Por que usar getLogger(__name__) em vez do logger raiz?
Porque um logger nomeado pelo módulo carrega a origem de cada mensagem, o que facilita filtrar e ajustar níveis por parte do sistema. Usar o logger raiz diretamente mistura tudo e tira essa granularidade. É a convenção recomendada em qualquer código sério.
Qual a diferença entre logger.error e logger.exception?
Ambos registram no nível ERROR, mas logger.exception, chamado dentro de um except, anexa o rastreamento completo da exceção automaticamente. Use exception dentro do tratamento de erros para não perder o rastro; use error quando quiser registrar uma falha sem o rastreamento.
Onde devo chamar basicConfig?
Uma vez só, no ponto de entrada da aplicação, antes de os logs começarem. Nunca dentro de uma biblioteca que outros vão importar, porque isso imporia uma configuração global a quem usa o seu código. A biblioteca só obtém loggers; a aplicação final decide a configuração.
Por que passar os valores como argumentos e não formatar a string?
Ao escrever logger.debug("x=%s", x), a formatação só ocorre se aquele nível for de fato registrado. Se você mesmo montar a string com f-string, o trabalho de formatar acontece sempre, mesmo quando a mensagem seria descartada. Passar argumentos evita esse custo desnecessário.
Posso mandar o log para o console e para um arquivo ao mesmo tempo?
Sim. Um logger pode ter vários handlers, cada um com destino, nível e formato próprios. É comum ter um handler de console mostrando INFO para cima e um handler de arquivo guardando desde DEBUG, para investigação posterior. Cada handler decide o que faz com a mensagem.

Fontes

Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.