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.
Ouvir o resumo desta aula
Um recap de cerca de 2 minutos na voz do Valim, para ouvir no trânsito ou na academia.
Ler a transcrição do resumo
Resumo da aula: O módulo logging: adeus, print.
Os objetivos desta aula. 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.
Veja o essencial, parte por parte.
Por que não usar print em produção. print manda tudo para a saída, sem níveis, sem destino configurável, sem contexto.
Níveis e o logger nomeado. Use logging.getLogger(__name__): o log herda o nome do módulo e facilita filtrar por origem.
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.
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
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.
- 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 NonegetLogger(__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.