Curso de Python / Investigar erros com traceback, depurador e logging

Para investigar um erro em Python, leia a exceção final do traceback e localize as chamadas do seu código, depois reproduza a entrada que causou a falha. Um depurador ajuda a observar o estado durante a execução; logging registra eventos com níveis de importância, permitindo diagnosticar o programa sem misturar todos os registros com sua saída normal.

Reduzir a falha a um caso reproduzível

O traceback mostra a sequência de chamadas relacionada à exceção. Comece pelo tipo e pela mensagem finais e procure os arquivos e linhas do seu projeto. Um erro visível dentro de uma biblioteca pode ter sido provocado por um argumento incorreto: confirme a entrada e a configuração antes de modificar código de terceiros.

Prepare uma versão pequena que ainda reproduza o problema e mantenha constantes as condições relevantes. Compare uma entrada válida com a que falha. Isso permite testar uma hipótese por vez e distinguir a causa de efeitos posteriores, como uma variável que deixou de ser calculada.

No VS Code, selecione o interpretador do projeto, abra o arquivo, coloque um ponto de interrupção na linha que chama a função e inicie a depuração de um arquivo Python. Ao parar antes da chamada, examine as variáveis e a pilha de chamadas; entre na função para observar seus parâmetros antes da verificação que pode falhar. Os detalhes de configuração e a seleção de ambiente são apresentados na prática de Python no VS Code.

Executar um cálculo com registros de log

Salve o programa completo em logging_demo.py, com UTF-8, numa pasta de prática. Execute python logging_demo.py num terminal novo, com o ambiente virtual selecionado se houver. Em macOS ou Ubuntu sem ambiente virtual, use python3 logging_demo.py quando necessário. O módulo logging pertence à biblioteca padrão.

import logging

logging.basicConfig(level=logging.INFO, format="%(levelname)s:%(message)s")
logger = logging.getLogger(__name__)

def calculate(amounts):
    logger.debug("count=%s", len(amounts))
    if any(x < 0 for x in amounts):
        raise ValueError("negative amount")
    return sum(amounts)

try:
    total = calculate([3500, 8000, 2500])
except ValueError:
    logger.exception("calculation failed")
    raise SystemExit(1)
else:
    logger.info("total=%s", total)
print("finished")

A saída padrão, stdout, contém:

finished

Separadamente, o registro INFO:total=14000 é enviado à saída padrão de erro, stderr, nesta configuração. O terminal normalmente mostra os dois canais juntos. finished significa “concluído”; a linha de log informa a soma. O bloco de saída acima representa somente stdout, não tudo o que um terminal pode exibir.

calculate() recebe três valores, verifica se algum é negativo e retorna a soma. Em caso de ValueError, logger.exception() registra a mensagem “calculation failed”, isto é, “o cálculo falhou”, incluindo o traceback da exceção atual. Em seguida, SystemExit(1) informa uma falha do processo e impede alcançar a impressão de finished.

Interpretar níveis, canais e código de saída

Nível Uso típico
DEBUG Detalhes para investigar o estado e as decisões do programa.
INFO Progresso normal ou resultados da operação.
WARNING Situação que exige atenção.
ERROR Falha de uma operação.

Use os níveis de forma consistente com a política da aplicação. No programa, level=logging.INFO deixa o DEBUG de contagem sem exibição. Ao mudar para logging.DEBUG, aparece também DEBUG:count=3. A contagem é três mesmo no teste com um valor negativo: ela descreve a quantidade recebida antes da validação, não quantos valores foram aprovados.

logger.info("total=%s", total) fornece o formato e o valor separadamente para o sistema de logs. A configuração define como os registros são apresentados e para onde vão. O tutorial de logging e níveis de eventos explica essas escolhas; gravar em arquivo exigiria também definir destino, rotação, retenção e acesso.

Um registro de nível ERROR não faz o processo terminar automaticamente. Neste exemplo, a falha é expressa por raise SystemExit(1); o caso válido termina com código 0. Depois de executar o comando, confira imediatamente $LASTEXITCODE no PowerShell ou echo $? num shell macOS/Ubuntu. A documentação de saída com SystemExit descreve o significado do código de término.

Se uma ferramenta capturar stdout e stderr separadamente, sua apresentação combinada pode sugerir uma ordem diferente. Compare cada canal e o código de saída, em vez de concluir que um cálculo terminou apenas porque uma mensagem apareceu antes de outra. Para repetir as mudanças de nível, execute o arquivo num processo novo: ambientes interativos ou aplicações que já configuraram tratadores podem fazer basicConfig() não alterar a configuração existente. A referência de basicConfig() detalha essa condição.

Provocar uma falha controlada e restaurar o programa

  1. Execute a versão original. Confira finished em stdout, INFO:total=14000 no log e código de saída 0.
  2. Troque somente 8000 por -1 na lista passada a calculate(). Execute de novo. Deve haver ERROR:calculation failed e um traceback terminando em ValueError: negative amount. finished não deve ser impresso e o código de saída deve ser 1.
  3. Mantenha o dado negativo e troque o nível para logging.DEBUG. Num processo novo, confira DEBUG:count=3 antes do registro de falha no mesmo canal de log. Essa informação ajuda a observar a entrada recebida, mas não elimina o erro.
  4. Restaure 8000, mantendo DEBUG para conferir a contagem e a soma normais. Depois restaure INFO e execute novamente para recuperar a saída original.

O tratamento de ValueError foi escolhido para a falha prevista nesse cálculo. Outros defeitos, como um texto misturado aos números, não devem ser silenciados por um tratador amplo vazio. A prática de exceções e propagação de falhas ajuda a decidir onde recuperar um erro e onde deixar que o chamador reaja.

Registre a operação e os valores necessários ao diagnóstico, sem incluir senhas, cabeçalhos de autenticação ou conjuntos completos de dados pessoais. Repetir grandes objetos em cada passagem de um laço também consome tempo e armazenamento. A orientação para corrigir uma entrada e o registro técnico para investigar uma falha têm públicos e objetivos diferentes; escolha o conteúdo de cada um deliberadamente.