
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.
Trilha de aprendizado · Nível 8 · Tutorial 7
Produzir registros de execução úteis para diagnóstico, com níveis apropriados, configuração centralizada e cuidado com informações sensíveis.
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
Escolher níveis e prever a filtragem
Classifique eventos pela severidade e determine quais registros atravessam os limiares INFO e WARNING. 2 min
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
Dar contexto ao formato dos registros
Configure e interprete linhas de log com horário, nível, origem e mensagem. 2 min
Escrever mensagens úteis sem expor dados
Construa mensagens parametrizadas com contexto diagnóstico suficiente, selecionando apenas dados seguros. 3 min
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
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
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

Passo 1 de 8
Diferencie o que deve aparecer para o usuário do que serve para acompanhar e investigar a execução do programa.
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.
Observe como a mesma operação produz informações para públicos e finalidades diferentes.

O resultado responde ao usuário; os registros ajudam a acompanhar o que ocorreu durante o processamento.
Exemplo
Imagine um programa que processa três lotes de números.
Para o usuário: Total calculado: 248
Para diagnóstico:
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
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.
Associe cada informação do processamento de lotes à finalidade mais adequada.
Toque em um item e depois no par correspondente.

Passo 2 de 8
Classifique eventos pela severidade e determine quais registros atravessam os limiares INFO e WARNING.
Os níveis formam uma ordem crescente de severidade: DEBUG → INFO → WARNING → ERROR → CRITICAL.

A severidade aumenta de DEBUG para CRITICAL; cada nível comunica um impacto diferente.
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
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 cada evento do processamento ao nível mais adequado.
Toque em um item e depois no par correspondente.
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.
A aplicação produz eventos DEBUG, INFO, WARNING, ERROR e CRITICAL. Qual alternativa descreve corretamente a filtragem?

Passo 3 de 8
Organize loggers por módulo e mantenha a configuração básica no ponto de início da aplicação.
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.

Cada módulo identifica a origem de seus eventos; o ponto de início configura o logger raiz e o destino comum.
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.
Salve este conteúdo em processamento.py:
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
Salve este conteúdo em app.py:
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()
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.
Execute uma vez com INFO; após editar a linha indicada para WARNING, execute novamente:
python app.py
# Depois de trocar INFO por WARNING em app.py:
python app.pyDica
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
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.
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).
Coloque as etapas na ordem em que a aplicação organiza e emite um evento de diagnóstico.

Passo 4 de 8
Configure e interprete linhas de log com horário, nível, origem e mensagem.
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.
Leia a linha da esquerda para a direita como quatro partes: quando ocorreu, qual é a gravidade, de onde veio e o que aconteceu.

Um registro contextualizado combina horário, nível, origem e mensagem.
Execute este script localmente para observar a linha emitida. O horário exibido será o da sua execução.
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
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
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.
Relacione cada campo ao contexto que ele acrescenta.
Toque em um item e depois no par correspondente.
Qual formato inclui horário, severidade, origem e mensagem, nessa ordem?

Passo 5 de 8
Construa mensagens parametrizadas com contexto diagnóstico suficiente, selecionando apenas dados seguros.
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.

À esquerda, dados em excesso ampliam a exposição; à direita, poucos campos seguros preservam o valor diagnóstico.
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.
import logging
logger = logging.getLogger(__name__)
referencia = "LOTE-204"
quantidade = 18
logger.info(
"Lote %s processado: %s itens",
referencia,
quantidade,
)Dica
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.
Atenção
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.
Os valores abaixo são fictícios. A primeira chamada expõe dados desnecessários; a segunda seleciona apenas referência e contagem.
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"],
)Complete com o argumento separado que corresponde ao segundo %s:
logger.info("Lote %s processado: %s itens", referencia, ______)
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
Use logger.exception no tratamento de uma falha para registrar contexto, nível ERROR e traceback sem confundir registro com resolução.
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.
O contexto da operação e o percurso técnico da falha se complementam.

A mensagem identifica a operação; o traceback preserva a origem e o percurso da exceção.
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.
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 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.
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
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.
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
Capture registros em testes com pytest e verifique mensagem, severidade, origem e informações de exceção sem depender do formato exibido no console.
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.

O teste executa a operação, seleciona os registros relevantes e verifica seus atributos — sem comparar a linha formatada do console.
Dica
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.
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.
Salve este conteúdo no arquivo processador.py.
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
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.
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.
Este arquivo contém os dois cenários completos.
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
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.
Complete a expressão que obtém a mensagem já interpolada de um objeto record: record.____
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
Integre configuração, eventos seguros, registro de exceções e testes com caplog em uma pequena aplicação local.
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.
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.

A aplicação apresenta resultados, os módulos registram eventos e os testes verificam os registros.
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.
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
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.
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()
Abra o terminal na pasta dos arquivos e execute:
python app.pyDica
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.
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.
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
A partir da pasta que contém app.py, processamento.py e tests, execute:
python -m pytest -qApó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).
Resumo
Você integrou as decisões centrais do tutorial em uma aplicação executável e em testes automatizados.
Parabéns! Você concluiu: Registrar eventos e exceções com logging
100 XP
Milhares de cursos online em vídeo, ebooks e áudiobooks.
Para testar seus conhecimentos no decorrer dos cursos online
Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail
Baixe nosso aplicativo pelo QR Code ou pelos links abaixo:.
+ de 10 milhões
de alunos
Certificado grátis e
válido em todo o Brasil
60 mil exercícios
gratuitos
4,8/5 classificação
nas lojas de apps
Cursos gratuitos em
vídeo, ebooks e audiobooks