Trilha de aprendizado · Nível 8 · Tutorial 7

Registrar eventos e exceções com logging

Produzir registros de execução úteis para diagnóstico, com níveis apropriados, configuração centralizada e cuidado com informações sensíveis.

  • Nível: Intermediário
  • Duração: 22 min
  • 8 passos
Registrar eventos e exceções com logging

O que você vai percorrer

  1. Separar resultados de registros de diagnóstico Diferencie o que deve aparecer para o usuário do que serve para acompanhar e investigar a execução do programa. 2 min
  2. Escolher níveis e prever a filtragem Classifique eventos pela severidade e determine quais registros atravessam os limiares INFO e WARNING. 2 min
  3. Configurar a aplicação e identificar os módulos Organize loggers por módulo e mantenha a configuração básica no ponto de início da aplicação. 3 min
  4. Dar contexto ao formato dos registros Configure e interprete linhas de log com horário, nível, origem e mensagem. 2 min
  5. Escrever mensagens úteis sem expor dados Construa mensagens parametrizadas com contexto diagnóstico suficiente, selecionando apenas dados seguros. 3 min
  6. Registrar exceções com o rastreamento Use logger.exception no tratamento de uma falha para registrar contexto, nível ERROR e traceback sem confundir registro com resolução. 3 min
  7. Verificar mensagens e níveis com caplog Capture registros em testes com pytest e verifique mensagem, severidade, origem e informações de exceção sem depender do formato exibido no console. 3 min
  8. Aplicar logging e validar o diagnóstico Integre configuração, eventos seguros, registro de exceções e testes com caplog em uma pequena aplicação local. 5 min

O que você vai aprender

  • Distinguir registros de diagnóstico de mensagens destinadas ao usuário.
  • Escolher níveis de logging conforme a importância de cada evento.
  • Configurar a emissão de registros na aplicação e registrar exceções com seu rastreamento.
  • Verificar mensagens e níveis emitidos durante um teste usando caplog.

Antes de começar

  • Verificar integração com arquivos temporários
  • Controlar a execução de módulos com __name__
  • Tratar exceções com try, except, else e finally

Passo 1 de 8

Separar resultados de registros de diagnóstico

Diferencie o que deve aparecer para o usuário do que serve para acompanhar e investigar a execução do programa.

Duas finalidades diferentes

O que o usuário vê não é todo o diagnóstico

Uma mensagem apresentada com print pode comunicar ao usuário o resultado que ele solicitou. Já um registro de diagnóstico descreve acontecimentos internos relevantes, como o início, a conclusão ou a falha de uma operação.

Esses registros permitem acompanhar a execução sem interrompê-la. Eles complementam testes e o depurador: cada ferramenta ajuda a investigar o programa de uma forma diferente.

Resultado e acompanhamento interno

Observe como a mesma operação produz informações para públicos e finalidades diferentes.

Diagrama de um processamento de lotes que gera um resultado para o usuário e uma sequência separada de eventos de diagnóstico.

O resultado responde ao usuário; os registros ajudam a acompanhar o que ocorreu durante o processamento.

Um processamento, informações distintas

Exemplo

Lotes numéricos fictícios

Imagine um programa que processa três lotes de números.

Para o usuário: Total calculado: 248

Para diagnóstico:

  • o processamento dos três lotes começou;
  • o segundo lote foi concluído;
  • o terceiro lote falhou por conter um valor inválido.

O total é o resultado solicitado. Os demais eventos ajudam a reconstruir o que aconteceu internamente. Isso não torna print inadequado: ele continua apropriado quando a informação faz parte da interface do programa.

Dica

Pergunta para decidir

Pergunte: “Esta informação ajuda o usuário a usar o programa ou ajuda a equipe a acompanhar e investigar sua execução?” A resposta indica a finalidade mais provável da mensagem.

Classifique as informações

Saída ou diagnóstico?

Associe cada informação do processamento de lotes à finalidade mais adequada.

Toque em um item e depois no par correspondente.

Passo 2 de 8

Escolher níveis e prever a filtragem

Classifique eventos pela severidade e determine quais registros atravessam os limiares INFO e WARNING.

A escala de severidade

Do detalhe à falha grave

Os níveis formam uma ordem crescente de severidade: DEBUG → INFO → WARNING → ERROR → CRITICAL.

  • DEBUG: detalhes úteis durante uma investigação.
  • INFO: acontecimentos normais e relevantes.
  • WARNING: situação inesperada ou possível problema, mas a operação ainda pode prosseguir.
  • ERROR: uma operação não pôde ser concluída.
  • CRITICAL: falha grave que pode comprometer a continuidade da aplicação.

Progressão dos níveis

Cinco estágios ascendentes representam eventos cada vez mais graves, desde um detalhe de diagnóstico até uma falha que ameaça a continuidade da aplicação.

A severidade aumenta de DEBUG para CRITICAL; cada nível comunica um impacto diferente.

O impacto define o nível

Classifique pelo que aconteceu

O nível depende do impacto do evento no contexto, não apenas da existência de uma exceção. No processamento de lotes numéricos, um lote vazio que pode ser ignorado permite continuar e pode justificar WARNING. Já a falha que impede processar um lote necessário justifica ERROR.

Uma exceção tratada pode resultar em níveis diferentes conforme suas consequências. O ponto central é perguntar: a operação continuou, falhou ou colocou toda a aplicação em risco?

Exemplo

Eventos no processamento de lotes

DEBUG: “Lote lote-17 contém 25 valores.”

INFO: “Lote lote-17 processado com sucesso.”

WARNING: “Lote lote-18 está vazio; seguindo para o próximo.”

ERROR: “Não foi possível processar o lote lote-19.”

CRITICAL: “Serviço de processamento não pôde ser iniciado; aplicação será encerrada.”

Associe evento e severidade

Qual nível representa cada evento?

Associe cada evento do processamento ao nível mais adequado.

Toque em um item e depois no par correspondente.

Preveja a filtragem

O limiar deixa passar os níveis mais severos

Um limiar permite a emissão de registros com severidade igual ou superior à definida. Assim, o limiar INFO bloqueia apenas DEBUG; o limiar WARNING bloqueia DEBUG e INFO.

Sem uma configuração que reduza esse limiar, o padrão do logging é WARNING.

Quais registros seriam emitidos?

A aplicação produz eventos DEBUG, INFO, WARNING, ERROR e CRITICAL. Qual alternativa descreve corretamente a filtragem?

Passo 3 de 8

Configurar a aplicação e identificar os módulos

Organize loggers por módulo e mantenha a configuração básica no ponto de início da aplicação.

Uma responsabilidade para cada parte

Loggers nos módulos, configuração na entrada

logging faz parte da biblioteca padrão. Cada módulo pode obter um logger com logging.getLogger(__name__): assim, o nome do logger corresponde à origem do evento.

Os módulos registram o que acontece, mas não decidem sozinhos como a aplicação inteira emitirá os registros. Essa configuração fica no ponto de início, com logging.basicConfig. Seu parâmetro level define o limiar inicial.

No arranjo básico, um logger de módulo sem nível próprio herda o nível efetivo do logger raiz e encaminha seus registros aos destinos configurados nele.

Emissão distribuída, configuração centralizada

Diagrama com módulos de uma aplicação encaminhando eventos para uma configuração central e, depois, para um console.

Cada módulo identifica a origem de seus eventos; o ponto de início configura o logger raiz e o destino comum.

Monte o exemplo local

Crie dois arquivos na mesma pasta

O módulo processamento.py apenas obtém seu logger e emite eventos. O arquivo app.py configura a aplicação e apresenta o resultado ao usuário.

A configuração fica dentro do caminho protegido por if __name__ == "__main__". Portanto, importar app de outro módulo não executa basicConfig nem inicia o programa.

processamento.py

Salve este conteúdo em processamento.py:

python
import logging

logger = logging.getLogger(__name__)


def processar_lote(valores):
    logger.debug("Iniciando processamento do lote")
    resultado = sum(valores)
    logger.info("Processamento concluído")
    return resultado

app.py

Salve este conteúdo em app.py:

python
import logging

from processamento import processar_lote


def main():
    total = processar_lote([10, 20, 30])
    print(f"Total: {total}")


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    main()

Compare duas novas execuções

Mude somente o limiar

No terminal, dentro da pasta dos arquivos, execute o programa. Com level=logging.INFO, o evento INFO passa pelo limiar, mas o DEBUG não.

Depois, altere a configuração em app.py para level=logging.WARNING e execute o script novamente. Agora os dois eventos do módulo ficam abaixo do limiar. O print, destinado ao usuário, continua exibindo o total.

Comandos

Execute uma vez com INFO; após editar a linha indicada para WARNING, execute novamente:

shell
python app.py

# Depois de trocar INFO por WARNING em app.py:
python app.py

Dica

O que basicConfig cria

Sem uma configuração de arquivo, basicConfig instala um handler de console no logger raiz e escreve os registros em stderr. O print usa stdout, então a ordem visual entre as duas saídas pode variar no terminal. Nenhum arquivo de log é criado automaticamente.

Dica

Use uma nova execução para comparar

basicConfig normalmente não altera a configuração quando o logger raiz já possui handlers. Por isso, compare os limiares iniciando uma nova execução do script após cada alteração, como nos comandos acima.

Verifique a organização

Relate o que você observou

O que apareceu na execução com limiar INFO e o que mudou com o limiar WARNING? Mencione também o resultado apresentado ao usuário.

Escreva pelo menos 40 caracteres (0/40).

Ordene o fluxo de um registro

Coloque as etapas na ordem em que a aplicação organiza e emite um evento de diagnóstico.

  1. O registro aceito é encaminhado ao handler de console, que o escreve em stderr.
  2. O ponto de início chama logging.basicConfig e define o limiar do logger raiz.
  3. O nível efetivo herdado do logger raiz determina se o evento passa pelo limiar.
  4. O módulo obtém seu logger com logging.getLogger(__name__).
  5. A função do módulo emite um evento pelo logger.

Passo 4 de 8

Dar contexto ao formato dos registros

Configure e interprete linhas de log com horário, nível, origem e mensagem.

Quatro pistas em uma linha

O formato acrescenta contexto

O parâmetro format de logging.basicConfig define como cada registro aparece no handler básico. Um formato útil pode reunir:

  • %(asctime)s: horário do registro;
  • %(levelname)s: nível de severidade;
  • %(name)s: nome do logger, que indica a origem;
  • %(message)s: mensagem do evento.

O horário ajuda a acompanhar a sequência dos acontecimentos; o nível ajuda a priorizá-los; e o nome do logger ajuda a localizar o módulo responsável.

Anatomia visual de um registro

Leia a linha da esquerda para a direita como quatro partes: quando ocorreu, qual é a gravidade, de onde veio e o que aconteceu.

Diagrama horizontal com quatro blocos representados por relógio, indicador de severidade, árvore de módulos e balão de mensagem.

Um registro contextualizado combina horário, nível, origem e mensagem.

Configurar e interpretar

Formato no ponto de início

Execute este script localmente para observar a linha emitida. O horário exibido será o da sua execução.

python
import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
)

logger = logging.getLogger(__name__)
logger.info("Processamento concluído")

Exemplo

Exemplo de saída

Uma execução pode produzir uma linha semelhante a esta:

2025-04-18 14:32:10,421 | INFO | __main__ | Processamento concluído

Nesse script executado diretamente, o nome do logger é __main__. Em um módulo importado, logging.getLogger(__name__) usaria o nome desse módulo.

Dica

Apresentação não muda o evento

Alterar format muda somente a apresentação do registro. Isso não transforma um evento INFO em ERROR, não altera o limiar configurado e não modifica o comportamento da aplicação.

Associe campo e significado

Campos do formato

Relacione cada campo ao contexto que ele acrescenta.

Toque em um item e depois no par correspondente.

Escolha um formato completo

Formato para diagnóstico

Qual formato inclui horário, severidade, origem e mensagem, nessa ordem?

Passo 5 de 8

Escrever mensagens úteis sem expor dados

Construa mensagens parametrizadas com contexto diagnóstico suficiente, selecionando apenas dados seguros.

Contexto na medida certa

O que torna uma mensagem útil?

Uma boa mensagem identifica o que aconteceu, qual foi o resultado e somente o contexto necessário para investigar o evento. Em um processamento de lotes, uma referência fictícia e uma contagem costumam ser mais úteis e seguras do que a entrada completa.

Compare a ideia de “registrar tudo” com a seleção intencional de poucos campos.

Comparação entre um registro sobrecarregado de dados pessoais e credenciais e um registro conciso com operação, referência e contagem.

À esquerda, dados em excesso ampliam a exposição; à direita, poucos campos seguros preservam o valor diagnóstico.

Parametrize a mensagem

Use marcadores %s no conteúdo da mensagem e passe os valores como argumentos separados. Assim, o logging pode adiar a interpolação até que a mensagem realmente precise ser emitida.

Esses marcadores pertencem à mensagem. Eles não são os campos nomeados do formato geral do registro, como %(levelname)s e %(name)s.

Mensagem e argumentos separados

python
import logging

logger = logging.getLogger(__name__)

referencia = "LOTE-204"
quantidade = 18

logger.info(
    "Lote %s processado: %s itens",
    referencia,
    quantidade,
)

Dica

A avaliação dos argumentos não é adiada

Os valores são interpolados posteriormente, mas as expressões usadas como argumentos são avaliadas antes da chamada. Em logger.debug("Resumo: %s", montar_resumo()), por exemplo, montar_resumo() é executada mesmo que o registro DEBUG não seja emitido.

Selecione dados seguros

Atenção

DEBUG também exige cuidado

Não registre senhas, tokens nem informações pessoais desnecessárias, nem mesmo em DEBUG. Registrar uma entrada ou um objeto completo pode incluir campos que você não pretendia expor.

Prefira campos específicos ao objeto completo

Os valores abaixo são fictícios. A primeira chamada expõe dados desnecessários; a segunda seleciona apenas referência e contagem.

python
entrada = {
    "referencia": "LOTE-204",
    "quantidade": 18,
    "email": "pessoa@example.test",
    "token": "TOKEN-FICTICIO-NAO-USAR",
}

# Evite: inclui todos os campos da entrada.
logger.debug("Entrada recebida: %s", entrada)

# Prefira: registra apenas o contexto necessário.
logger.info(
    "Lote %s recebido: %s itens",
    entrada["referencia"],
    entrada["quantidade"],
)

Pratique mensagens seguras

Complete a chamada

Complete com o argumento separado que corresponde ao segundo %s:

logger.info("Lote %s processado: %s itens", referencia, ______)

Reformule o registro

Uma entrada contém referencia, quantidade, email e token. Reformule logger.info("Entrada recebida: %s", entrada) para registrar que o lote foi recebido, preservando valor diagnóstico sem incluir dados pessoais ou credenciais.

Escreva pelo menos 40 caracteres (0/40).

Passo 6 de 8

Registrar exceções com o rastreamento

Use logger.exception no tratamento de uma falha para registrar contexto, nível ERROR e traceback sem confundir registro com resolução.

Mensagem e rastreamento cumprem papéis diferentes

Preserve o caminho da falha

Dentro de um bloco except, logger.exception(...) emite um registro de nível ERROR e inclui as informações da exceção em tratamento, com seu traceback.

A mensagem escrita por você informa qual operação falhou. O traceback mostra onde a exceção surgiu e por quais chamadas ela passou. Já logger.error(...), usado de forma comum, registra a mensagem, mas não acrescenta automaticamente esse rastreamento.

Anatomia do registro de uma exceção

O contexto da operação e o percurso técnico da falha se complementam.

Diagrama em que uma operação passa por várias chamadas, encontra uma falha e gera um registro com uma mensagem contextual e uma pilha de quadros do traceback.

A mensagem identifica a operação; o traceback preserva a origem e o percurso da exceção.

Registre durante o tratamento

Exemplo executável com uma falha controlada

Execute o script em uma nova execução. O valor fictício inválido provoca a falha, e o bloco except define como o programa continua.

python
import logging

logger = logging.getLogger(__name__)


def calcular_total(lote_id, valores):
    try:
        numeros = [int(valor) for valor in valores]
        return sum(numeros)
    except ValueError:
        logger.exception("Falha ao processar o lote %s", lote_id)
        return None


if __name__ == "__main__":
    logging.basicConfig(
        level=logging.INFO,
        format="%(levelname)s %(name)s: %(message)s",
    )

    total = calcular_total("L-42", ["10", "inválido", "30"])

    if total is None:
        print("Não foi possível calcular o total.")
    else:
        print(f"Total: {total}")

Exemplo

O que observar

O console recebe um registro ERROR com a mensagem contextual e, em seguida, o traceback da ValueError. A mensagem apresentada ao usuário permanece simples.

logger.exception apenas registrou a exceção. Quem determinou a continuidade foi o tratamento: neste exemplo, o return None e o if posterior.

Um registro no ponto com contexto

Evite duplicar a mesma falha

Registre a exceção em um ponto que conheça a operação que falhou e possa descrevê-la com contexto útil. Se uma camada apenas deixa a mesma exceção seguir para outra, registrar nas duas pode produzir tracebacks duplicados para uma única falha.

Também revise os dados envolvidos: o próprio texto de uma exceção pode revelar valores recebidos. Prefira dados fictícios e controlados nos testes e não acrescente senhas, tokens ou informações pessoais à mensagem.

Atenção

Registrar não é resolver

Depois de registrar, o bloco except ainda precisa definir o comportamento adequado: retornar um resultado previsto, continuar de forma segura ou permitir que a falha prossiga. A chamada de logging, sozinha, não corrige o problema nem escolhe esse fluxo.

Escolha a chamada e o local

Decisão de diagnóstico

Uma função de serviço captura ValueError, conhece a referência fictícia do lote e retorna None para indicar que a operação falhou. Qual opção registra a falha de forma mais útil?

Passo 7 de 8

Verificar mensagens e níveis com caplog

Capture registros em testes com pytest e verifique mensagem, severidade, origem e informações de exceção sem depender do formato exibido no console.

Do evento ao objeto capturado

Teste o conteúdo, não a aparência

caplog é uma fixture pronta do pytest: basta solicitá-la como parâmetro do teste. Ela captura registros como objetos, disponíveis em caplog.records.

Cada objeto permite verificar aspectos estáveis do evento:

  • record.name: nome do logger de origem;
  • record.levelno ou record.levelname: severidade;
  • record.getMessage(): mensagem com os argumentos já interpolados;
  • record.exc_info: informações da exceção, quando incluídas.

Use caplog.set_level para capturar níveis como INFO ou DEBUG, inclusive limitando o ajuste a um logger nomeado.

Fluxo de uma verificação com caplog

Diagrama mostrando uma função emitindo eventos para uma coleção de registros capturada pelo teste, que verifica origem, nível, mensagem e exceção.

O teste executa a operação, seleciona os registros relevantes e verifica seus atributos — sem comparar a linha formatada do console.

Dica

Preserve a captura do pytest

Nos testes, não chame logging.basicConfig nem force a substituição de handlers. O pytest já instala o mecanismo usado por caplog para capturar os registros.

Código que emitirá os eventos

Prepare o módulo local

Crie um arquivo chamado processador.py. A função emite eventos de sucesso e registra uma falha controlada com logger.exception. O identificador do lote é fictício e não contém dados pessoais.

processador.py

Salve este conteúdo no arquivo processador.py.

python
import logging

logger = logging.getLogger(__name__)


def calcular_media(lote_id, valores):
    logger.info("Iniciando lote %s", lote_id)

    try:
        media = sum(valores) / len(valores)
    except (TypeError, ZeroDivisionError):
        logger.exception("Falha ao processar lote %s", lote_id)
        return None

    logger.info("Lote %s concluído com média %.1f", lote_id, media)
    return media

Exemplo

O que será capturado

Para calcular_media("L-17", [6, 8]), a mensagem final obtida por getMessage() será Lote L-17 concluído com média 7.0. Os marcadores %s e %.1f já estarão interpolados no objeto consultado pelo teste.

Verifique sucesso e exceção

Crie os testes

Na mesma pasta, crie test_processador.py. Os testes ajustam a captura para o logger processador e selecionam somente os registros dessa origem. O primeiro verifica um evento INFO; o segundo confirma um ERROR com informações de exceção.

test_processador.py

Este arquivo contém os dois cenários completos.

python
import logging

from processador import calcular_media


def test_registra_conclusao_do_lote(caplog):
    caplog.set_level(logging.INFO, logger="processador")

    resultado = calcular_media("L-17", [6, 8])

    registros = [
        record
        for record in caplog.records
        if record.name == "processador"
    ]
    conclusao = next(
        record
        for record in registros
        if record.getMessage().startswith("Lote L-17 concluído")
    )

    assert resultado == 7
    assert conclusao.name == "processador"
    assert conclusao.levelno == logging.INFO
    assert conclusao.getMessage() == "Lote L-17 concluído com média 7.0"


def test_registra_excecao_do_lote_vazio(caplog):
    caplog.set_level(logging.ERROR, logger="processador")

    resultado = calcular_media("L-18", [])

    falhas = [
        record
        for record in caplog.records
        if record.name == "processador"
        and record.levelno == logging.ERROR
    ]

    assert resultado is None
    assert len(falhas) == 1
    assert falhas[0].getMessage() == "Falha ao processar lote L-18"
    assert falhas[0].exc_info is not None

Dica

Faça verificações resistentes

Execute python -m pytest -q. Evite comparar horário, linha completa formatada ou todo o traceback: esses detalhes podem variar sem alterar o evento que o teste realmente precisa garantir.

Consolide a verificação

Mensagem interpolada

Complete a expressão que obtém a mensagem já interpolada de um objeto record: record.____

Execute e interprete

Execute os testes localmente com python -m pytest -q. O que aconteceu? Explique também por que os testes verificam atributos de caplog.records em vez de comparar exatamente a saída mostrada no console.

Escreva pelo menos 60 caracteres (0/60).

Passo 8 de 8

Aplicar logging e validar o diagnóstico

Integre configuração, eventos seguros, registro de exceções e testes com caplog em uma pequena aplicação local.

Monte o módulo de processamento

Uma aplicação, dois tipos de saída

Crie uma pasta local para a prática. Nela, o módulo processamento.py calculará a média de lotes numéricos e registrará eventos internos. O resultado destinado ao usuário ficará sob responsabilidade de app.py. Usaremos apenas referências fictícias, contagens e resultados numéricos, sem registrar o conteúdo completo dos lotes.

Fluxo do diagnóstico

O ponto de entrada configura o logging e chama o módulo de processamento. Os resultados seguem para o usuário, enquanto os eventos seguem para o canal de diagnóstico. Nos testes, caplog inspeciona diretamente os registros emitidos.

Diagrama mostrando app.py configurando o logging e chamando processamento.py, com caminhos separados para resultados do usuário, registros de diagnóstico e captura por caplog.

A aplicação apresenta resultados, os módulos registram eventos e os testes verificam os registros.

processamento.py

Crie o arquivo processamento.py. A exceção é registrada uma única vez, no ponto que conhece a operação e sua referência fictícia, e depois é propagada para que o chamador decida o fluxo.

python
import logging


logger = logging.getLogger(__name__)


def processar_lote(referencia, numeros):
    logger.info(
        "Iniciando lote %s com %s itens",
        referencia,
        len(numeros),
    )

    try:
        media = sum(numeros) / len(numeros)
    except (TypeError, ZeroDivisionError):
        logger.exception(
            "Falha ao processar lote %s",
            referencia,
        )
        raise

    logger.info("Lote %s concluído", referencia)
    return media

Configure, execute e compare as saídas

app.py

Crie app.py na mesma pasta. A configuração permanece no ponto de entrada. O primeiro lote termina normalmente; o segundo produz uma falha controlada, registrada com traceback e convertida em uma mensagem curta para o usuário.

python
import logging

from processamento import processar_lote


def main():
    lotes = [
        ("LOTE-OK", [10, 20, 30]),
        ("LOTE-VAZIO", []),
    ]

    for referencia, numeros in lotes:
        try:
            media = processar_lote(referencia, numeros)
        except (TypeError, ZeroDivisionError):
            print(f"{referencia}: não foi possível calcular a média")
        else:
            print(f"{referencia}: média = {media:.1f}")


if __name__ == "__main__":
    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
    )
    main()

Execute em uma nova sessão

Abra o terminal na pasta dos arquivos e execute:

bash
python app.py

Dica

O que conferir

Você deve encontrar a média 20.0 na saída do usuário, registros INFO do lote bem-sucedido e um registro ERROR com traceback para o lote vazio. A mensagem curta de falha também será apresentada ao usuário. Como print escreve em stdout e o handler básico escreve em stderr, a ordem visual das linhas pode variar. Depois, troque temporariamente level=logging.INFO por level=logging.WARNING e execute novamente: os registros INFO devem desaparecer, mas os resultados de print e o ERROR com traceback permanecem. Restaure INFO ao terminar.

Valide os eventos com caplog

Teste o conteúdo, não a aparência

Crie a pasta tests e, dentro dela, o arquivo test_processamento.py. Os testes não chamam basicConfig: caplog captura os registros e permite verificar origem, nível, mensagem interpolada e presença das informações da exceção.

tests/test_processamento.py

python
import logging

import pytest

from processamento import processar_lote


def test_registra_inicio_e_conclusao(caplog):
    caplog.set_level(logging.INFO, logger="processamento")

    resultado = processar_lote("LOTE-TESTE", [10, 20, 30])

    eventos = [
        registro
        for registro in caplog.records
        if registro.name == "processamento"
    ]

    assert resultado == 20
    assert [
        (registro.levelname, registro.getMessage())
        for registro in eventos
    ] == [
        ("INFO", "Iniciando lote LOTE-TESTE com 3 itens"),
        ("INFO", "Lote LOTE-TESTE concluído"),
    ]
    assert all(registro.exc_info is None for registro in eventos)


def test_registra_falha_com_excecao(caplog):
    caplog.set_level(logging.ERROR, logger="processamento")

    with pytest.raises(ZeroDivisionError):
        processar_lote("LOTE-VAZIO", [])

    eventos = [
        registro
        for registro in caplog.records
        if registro.name == "processamento"
    ]

    assert len(eventos) == 1
    registro = eventos[0]
    assert registro.levelno == logging.ERROR
    assert registro.getMessage() == "Falha ao processar lote LOTE-VAZIO"
    assert registro.exc_info is not None

Execute os testes

A partir da pasta que contém app.py, processamento.py e tests, execute:

bash
python -m pytest -q

Relate as evidências

Revisão da execução

Após executar a aplicação e os testes, relate: 1) o efeito de trocar o limiar de INFO para WARNING; 2) a diferença entre as linhas de print e os registros; 3) a evidência de que a falha incluiu traceback; e 4) quais atributos ou métodos dos registros foram verificados com caplog.

Escreva pelo menos 180 caracteres (0/180).

Síntese final

Resumo

Decisões essenciais de logging

Você integrou as decisões centrais do tutorial em uma aplicação executável e em testes automatizados.

  • Registre eventos relevantes para diagnóstico e mantenha resultados e mensagens de interface separados.
  • Escolha o nível conforme o impacto do evento e considere o limiar que filtrará os registros.
  • Obtenha loggers por módulo e centralize basicConfig no ponto de entrada protegido por name.
  • Use contexto suficiente e seguro, sem senhas, tokens, dados pessoais ou objetos completos desnecessários.
  • Dentro de except, use logger.exception quando o traceback for necessário e evite registrar a mesma falha repetidamente.
  • Com caplog, verifique origem, nível, mensagem interpolada e exc_info, sem depender da formatação do console.

Tutorial concluído

Parabéns! Você concluiu: Registrar eventos e exceções com logging

Você agora consegue separar resultados de diagnóstico, escolher níveis, configurar logging, registrar exceções com traceback e validar os eventos com caplog.

100 XP

Baixe o Aplicativo agora para ter acesso a + de 5000 cursos gratuitos, exercícios, certificado e muito conteúdo sem pagar nada!

  • Cursos online 100% gratuitos do início ao fim

    Milhares de cursos online em vídeo, ebooks e áudiobooks.

  • Mais de 60 mil exercícios gratuitos

    Para testar seus conhecimentos no decorrer dos cursos online

  • Certificado Digital gratuito válido em todo o Brasil

    Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail

Aplicativo Cursa na tela de ebook, na tela de curso em vídeo e na tela de exercícios do curso, mais o certificado de conclusão de curso